📄 tport_type_udp.c
字号:
/* * This file is part of the Sofia-SIP package * * Copyright (C) 2006 Nokia Corporation. * * Contact: Pekka Pessi <pekka.pessi@nokia.com> * * 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_connect.c Transport using HTTP CONNECT. * * See tport.docs for more detailed description of tport interface. * * @author Pekka Pessi <Pekka.Pessi@nokia.com> * @author Martti Mela <Martti.Mela@nokia.com> * * @date Created: Fri Mar 24 08:45:49 EET 2006 ppessi */#include "config.h"#include "tport_internal.h"#if HAVE_IP_RECVERR || HAVE_IPV6_RECVERR#include <linux/types.h>#include <linux/errqueue.h>#include <sys/uio.h>#endif#include <stdlib.h>#include <time.h>#include <assert.h>#include <errno.h>#include <limits.h>/* ---------------------------------------------------------------------- *//* UDP */staticint tport_udp_init_client(tport_primary_t *pri, tp_name_t tpn[1], su_addrinfo_t *ai, tagi_t const *tags, char const **return_culprit);tport_vtable_t const tport_udp_client_vtable ={ "udp", tport_type_client, sizeof (tport_primary_t), tport_udp_init_client, NULL, NULL, NULL, sizeof (tport_t), NULL, NULL, NULL, NULL, NULL, tport_recv_dgram, tport_send_dgram,};tport_vtable_t const tport_udp_vtable ={ "udp", tport_type_local, sizeof (tport_primary_t), tport_udp_init_primary, NULL, NULL, NULL, sizeof (tport_t), NULL, NULL, NULL, NULL, NULL, tport_recv_dgram, tport_send_dgram,};static void tport_check_trunc(tport_t *tp, su_addrinfo_t *ai);int tport_udp_init_primary(tport_primary_t *pri, tp_name_t tpn[1], su_addrinfo_t *ai, tagi_t const *tags, char const **return_culprit){ unsigned rmem = 0, wmem = 0; int events = SU_WAIT_IN; int s; s = su_socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (s == SOCKET_ERROR) return *return_culprit = "socket", -1; pri->pri_primary->tp_socket = s; if (tport_bind_socket(s, ai, return_culprit) < 0) return -1;#if HAVE_IP_RECVERR if (ai->ai_family == AF_INET || ai->ai_family == AF_INET6) { int const one = 1; if (setsockopt(s, SOL_IP, IP_RECVERR, &one, sizeof(one)) < 0) { if (ai->ai_family == AF_INET) SU_DEBUG_3(("setsockopt(IPVRECVERR): %s\n", su_strerror(su_errno()))); } events |= SU_WAIT_ERR; }#endif#if HAVE_IPV6_RECVERR if (ai->ai_family == AF_INET6) { int const one = 1; if (setsockopt(s, SOL_IPV6, IPV6_RECVERR, &one, sizeof(one)) < 0) SU_DEBUG_3(("setsockopt(IPV6_RECVERR): %s\n", su_strerror(su_errno()))); events |= SU_WAIT_ERR; }#endif tl_gets(tags, TPTAG_UDP_RMEM_REF(rmem), TPTAG_UDP_WMEM_REF(wmem), TAG_END()); if (rmem != 0 && setsockopt(s, SOL_SOCKET, SO_RCVBUF, (void *)&rmem, sizeof rmem) < 0) { SU_DEBUG_3(("setsockopt(SO_RCVBUF): %s\n", su_strerror(su_errno()))); } if (wmem != 0 && setsockopt(s, SOL_SOCKET, SO_SNDBUF, (void *)&wmem, sizeof wmem) < 0) { SU_DEBUG_3(("setsockopt(SO_SNDBUF): %s\n", su_strerror(su_errno()))); } pri->pri_primary->tp_events = events; tport_init_compressor(pri->pri_primary, tpn->tpn_comp, tags); tport_check_trunc(pri->pri_primary, ai); tport_stun_server_add_socket(pri->pri_primary); return 0;}staticint tport_udp_init_client(tport_primary_t *pri, tp_name_t tpn[1], su_addrinfo_t *ai, tagi_t const *tags, char const **return_culprit){ pri->pri_primary->tp_conn_orient = 1; return 0;}/** Runtime test making sure MSG_TRUNC work as expected */static void tport_check_trunc(tport_t *tp, su_addrinfo_t *ai){#if HAVE_MSG_TRUNC int n; char buffer[2]; su_sockaddr_t su[1]; socklen_t sulen = sizeof su; n = sendto(tp->tp_socket, "TEST", 4, 0, (void *)ai->ai_addr, ai->ai_addrlen); for (;;) { n = recvfrom(tp->tp_socket, buffer, sizeof buffer, MSG_TRUNC, (void *)&su, &sulen); if (n > sizeof buffer) { tp->tp_trunc = 1; return; } /* XXX - check that su and tp->tp_addrinfo->ai_addr match */ return; }#endif}/** Receive datagram. * * @retval -1 error * @retval 0 end-of-stream * @retval 1 normal receive (should never happen) * @retval 2 incomplete recv, call me again (should never happen) * @retval 3 STUN keepalive, ignore */int tport_recv_dgram(tport_t *self){ msg_t *msg; int n, veclen; su_addrinfo_t *ai; su_sockaddr_t *from; socklen_t fromlen; msg_iovec_t iovec[msg_n_fragments] = {{ 0 }}; uint8_t sample[1]; /* Simulate packet loss */ if (self->tp_params->tpp_drop && su_randint(0, 1000) < self->tp_params->tpp_drop) { recv(self->tp_socket, sample, 1, 0); SU_DEBUG_3(("tport(%p): simulated packet loss!\n", self)); return 0; } assert(self->tp_msg == NULL); veclen = tport_recv_iovec(self, &self->tp_msg, iovec, 65536, 1); if (veclen < 0) return -1; msg = self->tp_msg; ai = msg_addrinfo(msg); from = (su_sockaddr_t *)ai->ai_addr, fromlen = ai->ai_addrlen; n = su_vrecv(self->tp_socket, iovec, veclen, 0, from, &fromlen); ai->ai_addrlen = fromlen; if (n == SOCKET_ERROR) { int error = su_errno(); msg_destroy(msg); self->tp_msg = NULL; su_seterrno(error); if (error == EAGAIN || error == EWOULDBLOCK) return 0; else return -1; } else if (n <= 1) { SU_DEBUG_1(("%s(%p): runt of %u bytes\n", "tport_recv_dgram", self, n)); msg_destroy(msg), self->tp_msg = NULL; return 0; } SU_CANONIZE_SOCKADDR(from); if (self->tp_master->mr_dump_file) tport_dump_iovec(self, msg, n, iovec, veclen, "recv", "from"); *sample = *((uint8_t *)iovec[0].mv_base); /* Commit received data into buffer. This may relocate iovec contents */ msg_recv_commit(msg, n, 1); if ((sample[0] & 0xf8) == 0xf8) /* SigComp */ return tport_recv_comp_dgram(self, self->tp_comp, &self->tp_msg, from, fromlen); else if (sample[0] == 0 || sample[0] == 1) /* STUN request or response */ return tport_recv_stun_dgram(self, &self->tp_msg, from, fromlen); else return 0;}/** Send using su_vsend(). Map IPv4 addresses as IPv6 addresses, if needed. */int tport_send_dgram(tport_t const *self, msg_t *msg, msg_iovec_t iov[], int iovused){ su_sockaddr_t su[1]; socklen_t sulen = sizeof su; if (tport_is_connection_oriented(self)) return su_vsend(self->tp_socket, iov, iovused, MSG_NOSIGNAL, NULL, 0); msg_get_address(msg, su, &sulen);#if SU_HAVE_IN6 && defined(IN6_INADDR_TO_V4MAPPED) if (su->su_family == AF_INET && self->tp_addrinfo->ai_family == AF_INET6) { su_sockaddr_t su0[1]; memset(su0, 0, sizeof su0); su0->su_family = self->tp_addrinfo->ai_family; su0->su_port = su->su_port; IN6_INADDR_TO_V4MAPPED(&su->su_sin.sin_addr, &su0->su_sin6.sin6_addr); memcpy(su, su0, sulen = sizeof(su0->su_sin6)); }#endif su_soerror(self->tp_socket); /* XXX - we *still* have a race condition */ return su_vsend(self->tp_socket, iov, iovused, MSG_NOSIGNAL, su, sulen);}#if !HAVE_IP_RECVERR && !HAVE_IPV6_RECVERR/** Process UDP error event. */int tport_udp_error(tport_t const *self, su_sockaddr_t name[1]){ if (tport_is_connection_oriented(self)) name[0] = self->tp_addr[0]; return su_soerror(self->tp_socket);}#else/** Process UDP error event. */int tport_udp_error(tport_t const *self, su_sockaddr_t name[1]){ struct cmsghdr *c; struct sock_extended_err *ee; su_sockaddr_t *from; char control[512]; char errmsg[64 + 768]; struct iovec iov[1]; struct msghdr msg[1] = {{ 0 }}; int n; msg->msg_name = name, msg->msg_namelen = sizeof(*name); msg->msg_iov = iov, msg->msg_iovlen = 1; iov->iov_base = errmsg, iov->iov_len = sizeof(errmsg); msg->msg_control = control, msg->msg_controllen = sizeof(control); n = recvmsg(self->tp_socket, msg, MSG_ERRQUEUE); if (n < 0) { int err = su_errno(); if (err != EAGAIN && err != EWOULDBLOCK) SU_DEBUG_1(("%s: recvmsg: %s\n", __func__, su_strerror(err))); return 0; } if ((msg->msg_flags & MSG_ERRQUEUE) != MSG_ERRQUEUE) { SU_DEBUG_1(("%s: recvmsg: no errqueue\n", __func__)); return 0; } if (msg->msg_flags & MSG_CTRUNC) { SU_DEBUG_1(("%s: extended error was truncated\n", __func__)); return 0; } if (msg->msg_flags & MSG_TRUNC) { /* ICMP message may contain original message... */ SU_DEBUG_3(("%s: icmp(6) message was truncated (at %d)\n", __func__, n)); } /* Go through the ancillary data */ for (c = CMSG_FIRSTHDR(msg); c; c = CMSG_NXTHDR(msg, c)) { if (0#if HAVE_IP_RECVERR || (c->cmsg_level == SOL_IP && c->cmsg_type == IP_RECVERR)#endif#if HAVE_IPV6_RECVERR || (c->cmsg_level == SOL_IPV6 && c->cmsg_type == IPV6_RECVERR)#endif ) { char info[128]; char const *origin; ee = (struct sock_extended_err *)CMSG_DATA(c); from = (su_sockaddr_t *)SO_EE_OFFENDER(ee); info[0] = '\0'; switch (ee->ee_origin) { case SO_EE_ORIGIN_LOCAL: origin = "local"; break; case SO_EE_ORIGIN_ICMP: origin = "icmp"; snprintf(info, sizeof(info), " type=%u code=%u", ee->ee_type, ee->ee_code); break; case SO_EE_ORIGIN_ICMP6: origin = "icmp6"; snprintf(info, sizeof(info), " type=%u code=%u", ee->ee_type, ee->ee_code); break; case SO_EE_ORIGIN_NONE: origin = "none"; break; default: origin = "unknown"; break; } if (ee->ee_info) snprintf(info + strlen(info), sizeof(info) - strlen(info), " info=%08x", ee->ee_info); SU_DEBUG_3(("%s: %s (%d) [%s%s]\n", __func__, su_strerror(ee->ee_errno), ee->ee_errno, origin, info)); if (from->su_family != AF_UNSPEC) SU_DEBUG_3(("\treported by [%s]:%u\n", inet_ntop(from->su_family, SU_ADDR(from), info, sizeof(info)), ntohs(from->su_port))); if (msg->msg_namelen == 0) name->su_family = AF_UNSPEC; SU_CANONIZE_SOCKADDR(name); return ee->ee_errno; } } return 0;}#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -