⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tport_type_udp.c

📁 Sofia SIP is an open-source SIP User-Agent library, compliant with the IETF RFC3261 specification.
💻 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_type_udp.c UDP Transport * * 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"#include "sofia-sip/hostdomain.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 <string.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);#if HAVE_FUNC#elif HAVE_FUNCTION#define __func__ __FUNCTION__#elsestatic char const __func__[] = "tport_type_udp";#endiftport_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;#if HAVE_IP_ADD_MEMBERSHIP  su_sockaddr_t *su = (su_sockaddr_t *)ai->ai_addr;#endif  int const one = 1; (void)one;  s = su_socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);  if (s == INVALID_SOCKET)    return *return_culprit = "socket", -1;  pri->pri_primary->tp_socket = s;  if (tport_bind_socket(s, ai, return_culprit) < 0)    return -1;  tport_set_tos(s, ai, pri->pri_params->tpp_tos);#if HAVE_IP_ADD_MEMBERSHIP  if (ai->ai_family == AF_INET &&       IN_MULTICAST(ntohl(su->su_sin.sin_addr.s_addr))) {    /* Try to join to the multicast group */    /* Bind to the SIP address like        <sip:88.77.66.55:5060;maddr=224.0.1.75;transport=udp> */    struct ip_mreq imr[1];    struct in_addr iface;    memset(imr, 0, sizeof imr);          imr->imr_multiaddr = su->su_sin.sin_addr;    if (host_is_ip4_address(tpn->tpn_canon) &&	su_inet_pton(AF_INET, tpn->tpn_canon, &iface) > 0) {      imr->imr_interface = iface;    }    if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, imr, (sizeof imr)) < 0) {      SU_DEBUG_3(("setsockopt(%s): %s\n",		  "IP_ADD_MEMBERSHIP", su_strerror(su_errno())));    }#if HAVE_IP_MULTICAST_LOOP    else       if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, &one, sizeof one) < 0) {	SU_DEBUG_3(("setsockopt(%s): %s\n",		    "IP_MULTICAST_LOOP", su_strerror(su_errno())));    }#endif  }#endif#if HAVE_IP_MTU_DISCOVER  {    /* Turn off DF flag on Linux */    int dont = IP_PMTUDISC_DONT;    if (setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &dont, sizeof(dont)) < 0) {	SU_DEBUG_3(("setsockopt(%s): %s\n",		    "IP_MTU_DISCOVER", su_strerror(su_errno())));    }  }#endif#if HAVE_IP_RECVERR  if (ai->ai_family == AF_INET || ai->ai_family == AF_INET6) {    if (setsockopt(s, IPPROTO_IP, IP_RECVERR, &one, sizeof(one)) < 0) {      if (ai->ai_family == AF_INET)	SU_DEBUG_3(("setsockopt(%s): %s\n",		    "IPVRECVERR", su_strerror(su_errno())));    }    events |= SU_WAIT_ERR;  }#endif#if HAVE_IPV6_RECVERR  if (ai->ai_family == AF_INET6) {    if (setsockopt(s, IPPROTO_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 && #if HAVE_SO_RCVBUFFORCE      setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, (void *)&rmem, sizeof rmem) < 0 &&#endif      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 && #if HAVE_SO_SNDBUFFORCE      setsockopt(s, SOL_SOCKET, SO_SNDBUFFORCE, (void *)&wmem, sizeof wmem) < 0 &&#endif      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);#if HAVE_SOFIA_STUN  tport_stun_server_add_socket(pri->pri_primary);#endif  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  ssize_t n;  char buffer[2];  su_sockaddr_t su[1];  socklen_t sulen = sizeof su;  n = su_sendto(tp->tp_socket,		"TEST", 4, 0,		(void *)ai->ai_addr, (socklen_t)ai->ai_addrlen);  if (n != 4)    return;  for (;;) {    n = su_recvfrom(tp->tp_socket, buffer, sizeof buffer, MSG_TRUNC, 		    (void *)&su, &sulen);    if (n > (ssize_t)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;  ssize_t n, veclen, N;  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 &&       (unsigned)su_randint(0, 1000) < self->tp_params->tpp_drop) {    su_recv(self->tp_socket, sample, 1, 0);    SU_DEBUG_3(("tport(%p): simulated packet loss!\n", (void *)self));    return 0;  }  assert(self->tp_msg == NULL);#if nomore  /* We used to resize the buffer, but it fragments the memory */  N = 65535;#else  N = (ssize_t)su_getmsgsize(self->tp_socket);  if (N == -1) {    int err = su_errno();    SU_DEBUG_1(("%s(%p): su_getmsgsize(): %s (%d)\n", __func__, (void *)self,		su_strerror(err), err));    return -1;  }  if (N == 0) {    su_recv(self->tp_socket, sample, 1, 0);    SU_DEBUG_3(("tport(%p): zero length packet", (void *)self));    return 0;  }#endif  veclen = tport_recv_iovec(self, &self->tp_msg, iovec, N, 1);  if (veclen == -1)    return -1;  msg = self->tp_msg;  ai = msg_addrinfo(msg);  from = (su_sockaddr_t *)ai->ai_addr, fromlen = (socklen_t)(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 (su_is_blocking(error))      return 0;    else      return -1;  }  else if (n <= 1) {    SU_DEBUG_1(("%s(%p): runt of "MOD_ZD" bytes\n",		"tport_recv_dgram", (void *)self, n));    msg_destroy(msg), self->tp_msg = NULL;    return 0;  }  tport_recv_bytes(self, n, n);  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);#if HAVE_SOFIA_STUN  else if (sample[0] == 0 || sample[0] == 1)    /* STUN request or response */    return tport_recv_stun_dgram(self, &self->tp_msg, from, fromlen);#endif  else    return 0;}/** Send using su_vsend(). Map IPv4 addresses as IPv6 addresses, if needed. */ssize_t tport_send_dgram(tport_t const *self, msg_t *msg, 			 msg_iovec_t iov[], 			 size_t 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 (!su_is_blocking(err))      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 == IPPROTO_IP && c->cmsg_type == IP_RECVERR)#endif#if HAVE_IPV6_RECVERR	|| (c->cmsg_level == IPPROTO_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",		    su_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 + -