/* * This file is part of the Sofia-SIP package * * Copyright (C) 2006 Nokia Corporation. * * Contact: Pekka Pessi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ /**@CFILE tport_type_connect.c Transport using HTTP CONNECT. * * See tport.docs for more detailed description of tport interface. * * @author Pekka Pessi * @author Martti Mela * * @date Created: Fri Mar 24 08:45:49 EET 2006 ppessi */ #include "config.h" #include "tport_internal.h" #include #include #include #include #include /* ---------------------------------------------------------------------- */ /* TCP using HTTP CONNECT */ #include #include static int tport_http_connect_init_primary(tport_primary_t *, tp_name_t tpn[1], su_addrinfo_t *, tagi_t const *, char const **return_culprit); static void tport_http_connect_deinit_primary(tport_primary_t *); static tport_t *tport_http_connect(tport_primary_t *pri, su_addrinfo_t *ai, tp_name_t const *tpn); static void tport_http_deliver(tport_t *self, msg_t *msg, su_time_t now); typedef struct { tport_primary_t thc_primary[1]; su_addrinfo_t *thc_proxy; } tport_http_connect_t; typedef struct { tport_t thci_tport[1]; msg_t *thci_response; msg_t *thci_stackmsg; } tport_http_connect_instance_t; tport_vtable_t const tport_http_connect_vtable = { "TCP", tport_type_connect, sizeof (tport_http_connect_t), tport_http_connect_init_primary, tport_http_connect_deinit_primary, NULL, tport_http_connect, sizeof (tport_http_connect_instance_t), NULL, NULL, NULL, NULL, NULL, tport_recv_stream, tport_send_stream, tport_http_deliver, }; static int tport_http_connect_init_primary(tport_primary_t *pri, tp_name_t tpn[1], su_addrinfo_t *ai, tagi_t const *tags, char const **return_culprit) { tport_http_connect_t *thc = (tport_http_connect_t *)pri; char const *http_connect = NULL; url_t *http_proxy; int error; char const *host, *port; su_addrinfo_t hints[1]; tl_gets(tags, TPTAG_HTTP_CONNECT_REF(http_connect), TAG_END()); if (!http_connect) return *return_culprit = "missing proxy url", -1; http_proxy = url_hdup(pri->pri_home, URL_STRING_MAKE(http_connect)->us_url); if (!http_proxy || !http_proxy->url_host) return *return_culprit = "invalid proxy url", -1; host = http_proxy->url_host; port = http_proxy->url_port; if (!port || !port[0]) port = "8080"; memcpy(hints, ai, sizeof hints); hints->ai_flags = 0; hints->ai_addr = NULL; hints->ai_addrlen = 0; hints->ai_next = NULL; hints->ai_canonname = NULL; error = su_getaddrinfo(host, port, hints, &thc->thc_proxy); if (error) return *return_culprit = "su_getaddrinfo", -1; return tport_tcp_init_client(pri, tpn, ai, tags, return_culprit); } static void tport_http_connect_deinit_primary(tport_primary_t *pri) { tport_http_connect_t *thc = (tport_http_connect_t *)pri; su_freeaddrinfo(thc->thc_proxy), thc->thc_proxy = NULL; } static tport_t *tport_http_connect(tport_primary_t *pri, su_addrinfo_t *ai, tp_name_t const *tpn) { tport_http_connect_t *thc = (tport_http_connect_t *)pri; tport_http_connect_instance_t *thci; tport_master_t *mr = pri->pri_master; msg_t *msg, *response; char hostport[TPORT_HOSTPORTSIZE]; tport_t *tport; http_request_t *rq; msg = msg_create(http_default_mclass(), 0); if (!msg) return NULL; tport_hostport(hostport, sizeof hostport, (void *)ai->ai_addr, 1); rq = http_request_format(msg_home(msg), "CONNECT %s HTTP/1.1", hostport); if (msg_header_insert(msg, NULL, (void *)rq) < 0 || msg_header_add_str(msg, NULL, "User-Agent: Sofia-SIP/" VERSION "\n") < 0 || msg_header_add_str(msg, NULL, "Proxy-Connection: keepalive\n") < 0 || msg_header_add_make(msg, NULL, http_host_class, hostport) < 0 || msg_header_add_make(msg, NULL, http_separator_class, "\r\n") < 0 || msg_serialize(msg, NULL) < 0 || msg_prepare(msg) < 0) return (void)msg_destroy(msg), NULL; /* * Create a response message that ignores the body * if there is no Content-Length */ response = msg_create(http_default_mclass(), mr->mr_log | MSG_FLG_MAILBOX); tport = tport_base_connect(pri, thc->thc_proxy, ai, tpn); if (!tport) { msg_destroy(msg); msg_destroy(response); return tport; } thci = (tport_http_connect_instance_t*)tport; thci->thci_response = response; tport->tp_msg = response; msg_set_next(response, thci->thci_stackmsg = tport_msg_alloc(tport, 512)); if (tport_send_msg(tport, msg, tpn, NULL) < 0) { SU_DEBUG_9(("tport_send_msg failed in tpot_http_connect\n")); msg_destroy(msg); tport_zap_secondary(tport); return NULL; } return tport; } #include static void tport_http_deliver(tport_t *self, msg_t *msg, su_time_t now) { tport_http_connect_instance_t *thci = (tport_http_connect_instance_t*)self; if (msg && thci->thci_response == msg) { tport_http_connect_t *thc = (tport_http_connect_t *)self->tp_pri; http_t *http = http_object(msg); if (http && http->http_status) { SU_DEBUG_0(("tport_http_connect: %u %s\n", http->http_status->st_status, http->http_status->st_phrase)); if (http->http_status->st_status < 300) { msg_buf_move(thci->thci_stackmsg, msg); thci->thci_response = NULL; thci->thci_stackmsg = NULL; return; } } msg_destroy(msg); thci->thci_response = NULL; tport_error_report(self, EPROTO, (void *)thc->thc_proxy->ai_addr); tport_close(self); return; } tport_base_deliver(self, msg, now); }