/** receives an ipv4 packet using a raw socket. * An ipv4 packet is received in buf, using IP_PKTINFO or IP_RECVDSTADDR. * from and to are filled (only the ip part the ports are 0 since this * function doesn't try to look beyond the IP level). * @param sock - raw socket * @param buf - detination buffer. * @param len - buffer len (should be enough for receiving a packet + * IP header). * @param from - result parameter, the IP address part of it will be filled * with the source address and the port with 0. * @param to - result parameter, the IP address part of it will be filled * with the destination (local) address and the port with 0. * @return packet len or <0 on error: -1 (check errno), * -2 no IP_PKTINFO/IP_RECVDSTADDR found or AF mismatch */ int recvpkt4(int sock, char* buf, int len, union sockaddr_union* from, union sockaddr_union* to) { struct iovec iov[1]; struct msghdr rcv_msg; struct cmsghdr* cmsg; #ifdef IP_PKTINFO struct in_pktinfo* rcv_pktinfo; #endif /* IP_PKTINFO */ int n, ret; char msg_ctrl_buf[1024]; iov[0].iov_base=buf; iov[0].iov_len=len; rcv_msg.msg_name=from; rcv_msg.msg_namelen=sockaddru_len(*from); rcv_msg.msg_control=msg_ctrl_buf; rcv_msg.msg_controllen=sizeof(msg_ctrl_buf); rcv_msg.msg_iov=&iov[0]; rcv_msg.msg_iovlen=1; ret=-2; /* no PKT_INFO or AF mismatch */ retry: n=recvmsg(sock, &rcv_msg, MSG_WAITALL); if (unlikely(n==-1)){ if (errno==EINTR) goto retry; ret=n; goto end; } /* find the pkt info */ for (cmsg=CMSG_FIRSTHDR(&rcv_msg); cmsg; cmsg=CMSG_NXTHDR(&rcv_msg, cmsg)){ #ifdef IP_PKTINFO if (likely((cmsg->cmsg_level==IPPROTO_IP) && (cmsg->cmsg_type==IP_PKTINFO))) { rcv_pktinfo=(struct in_pktinfo*)CMSG_DATA(cmsg); to->sin.sin_family=AF_INET; memcpy(&to->sin.sin_addr, &rcv_pktinfo->ipi_spec_dst.s_addr, sizeof(to->sin.sin_addr)); to->sin.sin_port=0; /* not known */ /* interface no. in ipi_ifindex */ ret=n; /* success */ break; } #elif defined (IP_RECVDSTADDR) if (likely((cmsg->cmsg_level==IPPROTO_IP) && (cmsg->cmsg_type==IP_RECVDSTADDR))) { to->sin.sin_family=AF_INET; memcpy(&to->sin.sin_addr, CMSG_DATA(cmsg), sizeof(to->sin.sin_addr)); to->sin.sin_port=0; /* not known */ ret=n; /* success */ break; } #else #error "no method of getting the destination ip address supported" #endif /* IP_PKTINFO / IP_RECVDSTADDR */ } end: return ret; }
static int jsonrpc_get_fd(union sockaddr_union *addr) { int fd; int flags; /* writing the iov on the network */ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { LM_ERR("cannot create socket\n"); return -1; } /* mark the socket as non-blocking after connect :) */ flags = fcntl(fd, F_GETFL); if (flags == -1) { LM_ERR("fcntl failed: %s\n", strerror(errno)); goto close; } if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) { LM_ERR("fcntl: set non-blocking failed: %s\n", strerror(errno)); goto close; } if (tcp_connect_blocking_timeout(fd, &addr->s, sockaddru_len(*addr), jrpc_connect_timeout) < 0) { LM_ERR("cannot connect to %s[%d:%s]\n", inet_ntoa(addr->sin.sin_addr), errno, strerror(errno)); goto close; } return fd; close: shutdown(fd, SHUT_RDWR); close(fd); return -1; }
/*! \brief which socket to use? main socket or new one? */ int sctp_server_send(struct socket_info *source, char *buf, unsigned len, union sockaddr_union* to) { int n; int tolen; tolen=sockaddru_len(*to); again: n=sctp_sendmsg(source->socket, buf, len, &to->s, tolen, 0, 0, 0, 0, 0); #ifdef XL_DEBUG LM_INFO("send status: %d\n", n); #endif if (n==-1){ LM_ERR("sctp_sendmsg(sock,%p,%d,%p,%d,0,0,0,0,0): %s(%d)\n", buf,len,&to->s,tolen, strerror(errno),errno); if (errno==EINTR) goto again; if (errno==EINVAL) { LM_CRIT("invalid sendtoparameters\n" "one possible reason is the server is bound to localhost and\n" "attempts to send to the net\n"); } } return n; }
/** * bin_send - computes the checksum of the current packet and then * sends the packet over UDP to the @dest destination * * @return: number of bytes sent, or -1 on error */ int bin_send(union sockaddr_union *dest) { int rc, destlen; str st; if (!dest) return 0; st.s = send_buffer + HEADER_SIZE; st.len = bin_send_size - HEADER_SIZE; /* compute a checksum of the binary packet content */ crc32_uint(&st, (unsigned int *)(send_buffer + BIN_PACKET_MARKER_SIZE)); LM_DBG("sending packet {'%.*s', %d}: %.*s [%d B] from socket %d\n", *(int *)(send_buffer + HEADER_SIZE), send_buffer + HEADER_SIZE + LEN_FIELD_SIZE, bin_send_type, bin_send_size, send_buffer, bin_send_size, bin->socket); destlen=sockaddru_len(*dest); again: rc=sendto(bin->socket, send_buffer, bin_send_size, 0, &dest->s, destlen); if (rc==-1){ if (errno==EINTR) goto again; LM_ERR("sendto() failed with %s(%d)\n", strerror(errno),errno); } return rc; }
int proto_sctp_read(struct socket_info *si, int* bytes_read) { struct receive_info ri; int len; #ifdef DYN_BUF char* buf; #else static char buf [BUF_SIZE+1]; #endif char *tmp; unsigned int fromlen; struct sctp_sndrcvinfo sinfo; #ifdef DYN_BUF buf=pkg_malloc(BUF_SIZE+1); if (buf==0) { LM_ERR(" could not allocate receive buffer in pkg memory\n"); goto error; } #endif fromlen=sockaddru_len(si->su); len = sctp_recvmsg(si->socket, buf, BUF_SIZE, &ri.src_su.s, &fromlen, &sinfo, 0); if (len==-1) { if (errno==EAGAIN) { LM_DBG("packet with bad checksum received\n"); return 0; } if ((errno==EINTR)||(errno==EWOULDBLOCK)|| (errno==ECONNREFUSED)) return -1; LM_ERR("sctp_recvmsg:[%d] %s\n", errno, strerror(errno)); return -2; } /* we must 0-term the messages, receive_msg expects it */ buf[len]=0; /* no need to save the previous char */ ri.bind_address = si; ri.dst_port = si->port_no; ri.dst_ip = si->address; ri.proto = si->proto; ri.proto_reserved1 = ri.proto_reserved2 = 0; su2ip_addr(&ri.src_ip, &ri.src_su); ri.src_port=su_getport(&ri.src_su); if (ri.src_port==0) { tmp=ip_addr2a(&ri.src_ip); LM_INFO("dropping 0 port packet from %s\n", tmp); return 0; } /* receive_msg must free buf too!*/ receive_msg(buf, len, &ri); return 0; }
struct tcp_connection* tcpconn_connect(union sockaddr_union* server, int type) { int s; struct socket_info* si; union sockaddr_union my_name; socklen_t my_name_len; struct tcp_connection* con; struct ip_addr ip; s=socket(AF2PF(server->s.sa_family), SOCK_STREAM, 0); if (s==-1){ LOG(L_ERR, "ERROR: tcpconn_connect: socket: (%d) %s\n", errno, strerror(errno)); goto error; } if (init_sock_opt(s)<0){ LOG(L_ERR, "ERROR: tcpconn_connect: init_sock_opt failed\n"); goto error; } if (tcp_blocking_connect(s, &server->s, sockaddru_len(*server))<0){ LOG(L_ERR, "ERROR: tcpconn_connect: tcp_blocking_connect failed\n"); goto error; } my_name_len=sizeof(my_name); if (getsockname(s, &my_name.s, &my_name_len)!=0){ LOG(L_ERR, "ERROR: tcp_connect: getsockname failed: %s(%d)\n", strerror(errno), errno); si=0; /* try to go on */ } su2ip_addr(&ip, &my_name); #ifdef USE_TLS if (type==PROTO_TLS) si=find_si(&ip, 0, PROTO_TLS); else #endif si=find_si(&ip, 0, PROTO_TCP); if (si==0){ LOG(L_ERR, "ERROR: tcp_connect: could not find corresponding" " listening socket, using default...\n"); if (server->s.sa_family==AF_INET) si=sendipv4_tcp; #ifdef USE_IPV6 else si=sendipv6_tcp; #endif } con=tcpconn_new(s, server, si, type, S_CONN_CONNECT); if (con==0){ LOG(L_ERR, "ERROR: tcp_connect: tcpconn_new failed, closing the " " socket\n"); goto error; } return con; /*FIXME: set sock idx! */ error: if (s!=-1) close(s); /* close the opened socket */ return 0; }
static struct tcp_connection* ws_sync_connect(struct socket_info* send_sock, union sockaddr_union* server) { int s; union sockaddr_union my_name; socklen_t my_name_len; struct tcp_connection* con; s=socket(AF2PF(server->s.sa_family), SOCK_STREAM, 0); if (s==-1){ LM_ERR("socket: (%d) %s\n", errno, strerror(errno)); goto error; } if (tcp_init_sock_opt(s)<0){ LM_ERR("tcp_init_sock_opt failed\n"); goto error; } my_name_len = sockaddru_len(send_sock->su); memcpy( &my_name, &send_sock->su, my_name_len); su_setport( &my_name, 0); if (bind(s, &my_name.s, my_name_len )!=0) { LM_ERR("bind failed (%d) %s\n", errno,strerror(errno)); goto error; } if (tcp_connect_blocking(s, &server->s, sockaddru_len(*server))<0){ LM_ERR("tcp_blocking_connect failed\n"); goto error; } con=tcp_conn_new(s, server, send_sock, S_CONN_OK); if (con==NULL){ LM_ERR("tcp_conn_create failed, closing the socket\n"); goto error; } /* it is safe to move this here and clear it after we complete the * handshake, just before sending the fd to main */ con->fd = s; return con; error: /* close the opened socket */ if (s!=-1) close(s); return 0; }
static struct tcp_connection* hep_sync_connect(struct socket_info* send_sock, union sockaddr_union* server, int *fd) { int s; union sockaddr_union my_name; socklen_t my_name_len; struct tcp_connection* con; s=socket(AF2PF(server->s.sa_family), SOCK_STREAM, 0); if (s==-1){ LM_ERR("socket: (%d) %s\n", errno, strerror(errno)); goto error; } if (tcp_init_sock_opt(s)<0){ LM_ERR("tcp_init_sock_opt failed\n"); goto error; } my_name_len = sockaddru_len(send_sock->su); memcpy( &my_name, &send_sock->su, my_name_len); su_setport( &my_name, 0); if (bind(s, &my_name.s, my_name_len )!=0) { LM_ERR("bind failed (%d) %s\n", errno,strerror(errno)); goto error; } if (tcp_connect_blocking(s, &server->s, sockaddru_len(*server))<0){ LM_ERR("tcp_blocking_connect failed\n"); goto error; } con = tcp_conn_create(s, server, send_sock, S_CONN_OK); if (con==NULL){ LM_ERR("tcp_conn_create failed, closing the socket\n"); goto error; } *fd = s; return con; /*FIXME: set sock idx! */ error: /* close the opened socket */ if (s!=-1) close(s); return 0; }
/** send an udp packet over a non-ip_hdrincl raw socket. * @param rsock - raw socket * @param buf - data * @param len - data len * @param from - source address:port (_must_ be non-null, but the ip address * can be 0, in which case it will be filled by the kernel). * @param to - destination address:port * @return <0 on error (errno set too), number of bytes sent on success * (including the udp header => on success len + udpheader size). */ int raw_udp4_send(int rsock, char* buf, unsigned int len, union sockaddr_union* from, union sockaddr_union* to) { struct msghdr snd_msg; struct cmsghdr* cmsg; #ifdef IP_PKTINFO struct in_pktinfo* snd_pktinfo; #endif /* IP_PKTINFO */ struct iovec iov[2]; struct udphdr udp_hdr; char msg_ctrl_snd_buf[1024]; int ret; memset(&snd_msg, 0, sizeof(snd_msg)); snd_msg.msg_name=&to->sin; snd_msg.msg_namelen=sockaddru_len(*to); snd_msg.msg_iov=&iov[0]; /* prepare udp header */ mk_udp_hdr(&udp_hdr, &from->sin, &to->sin, (unsigned char*)buf, len, 1); iov[0].iov_base=(char*)&udp_hdr; iov[0].iov_len=sizeof(udp_hdr); iov[1].iov_base=buf; iov[1].iov_len=len; snd_msg.msg_iovlen=2; snd_msg.msg_control=msg_ctrl_snd_buf; snd_msg.msg_controllen=sizeof(msg_ctrl_snd_buf); /* init pktinfo cmsg */ cmsg=CMSG_FIRSTHDR(&snd_msg); cmsg->cmsg_level=IPPROTO_IP; #ifdef IP_PKTINFO cmsg->cmsg_type=IP_PKTINFO; cmsg->cmsg_len=CMSG_LEN(sizeof(struct in_pktinfo)); snd_pktinfo=(struct in_pktinfo*)CMSG_DATA(cmsg); snd_pktinfo->ipi_ifindex=0; snd_pktinfo->ipi_spec_dst.s_addr=from->sin.sin_addr.s_addr; #elif defined (IP_SENDSRCADDR) cmsg->cmsg_type=IP_SENDSRCADDR; cmsg->cmsg_len=CMSG_LEN(sizeof(struct in_addr)); memcpy(CMSG_DATA(cmsg), &from->sin.sin_addr.s_addr, sizeof(struct in_addr)); #else #error "no method of setting the source ip supported" #endif /* IP_PKTINFO / IP_SENDSRCADDR */ snd_msg.msg_controllen=cmsg->cmsg_len; snd_msg.msg_flags=0; ret=sendmsg(rsock, &snd_msg, 0); return ret; }
/** * Main UDP send function, called from msg_send. * \see msg_send * \param source send socket * \param buf sent data * \param len data length in bytes * \param to destination address * \return -1 on error, the return value from sento on success */ static int proto_udp_send(struct socket_info* source, char* buf, unsigned int len, union sockaddr_union* to, int id) { int n, tolen; tolen=sockaddru_len(*to); again: n=sendto(source->socket, buf, len, 0, &to->s, tolen); if (n==-1){ LM_ERR("sendto(sock,%p,%d,0,%p,%d): %s(%d)\n", buf,len,to,tolen, strerror(errno),errno); if (errno==EINTR || errno==EAGAIN) goto again; if (errno==EINVAL) { LM_CRIT("invalid sendtoparameters\n" "one possible reason is the server is bound to localhost and\n" "attempts to send to the net\n"); } } return n; }
/*! \brief return a socket_info_pointer to the sending socket * \note As opposed to * get_send_socket(), which returns process's default socket, get_out_socket * attempts to determine the outbound interface which will be used; * it creates a temporary connected socket to determine it; it will * be very likely noticeably slower, but it can deal better with * multihomed hosts */ struct socket_info* get_out_socket(union sockaddr_union* to, int proto) { int temp_sock; socklen_t len; union sockaddr_union from; struct socket_info* si; struct ip_addr ip; if (proto!=PROTO_UDP) { LM_CRIT("can only be called for UDP\n"); return 0; } temp_sock=socket(to->s.sa_family, SOCK_DGRAM, 0 ); if (temp_sock==-1) { LM_ERR("socket() failed: %s\n", strerror(errno)); return 0; } if (connect(temp_sock, &to->s, sockaddru_len(*to))==-1) { LM_ERR("connect failed: %s\n", strerror(errno)); goto error; } len=sizeof(from); if (getsockname(temp_sock, &from.s, &len)==-1) { LM_ERR("getsockname failed: %s\n", strerror(errno)); goto error; } su2ip_addr(&ip, &from); si=find_si(&ip, 0, proto); if (si==0) goto error; close(temp_sock); LM_DBG("socket determined: %p\n", si ); return si; error: LM_ERR("no socket found\n"); close(temp_sock); return 0; }
static int hep_udp_read_req(struct socket_info *si, int* bytes_read) { struct receive_info ri; int len; #ifdef DYN_BUF char* buf; #else static char buf [BUF_SIZE+1]; #endif char *tmp; unsigned int fromlen; str msg; struct hep_desc h; int ret = 0; #ifdef DYN_BUF buf=pkg_malloc(BUF_SIZE+1); if (buf==0){ LM_ERR("could not allocate receive buffer\n"); goto error; } #endif fromlen=sockaddru_len(si->su); len=recvfrom(bind_address->socket, buf, BUF_SIZE,0,&ri.src_su.s,&fromlen); if (len==-1){ if (errno==EAGAIN) return 0; if ((errno==EINTR)||(errno==EWOULDBLOCK)|| (errno==ECONNREFUSED)) return -1; LM_ERR("recvfrom:[%d] %s\n", errno, strerror(errno)); return -2; } if (len<MIN_UDP_PACKET) { LM_DBG("probing packet received len = %d\n", len); return 0; } /* we must 0-term the messages, receive_msg expects it */ buf[len]=0; /* no need to save the previous char */ ri.bind_address = si; ri.dst_port = si->port_no; ri.dst_ip = si->address; ri.proto = si->proto; ri.proto_reserved1 = ri.proto_reserved2 = 0; su2ip_addr(&ri.src_ip, &ri.src_su); ri.src_port=su_getport(&ri.src_su); msg.s = buf; msg.len = len; /* if udp we are sure that version 1 or 2 of the * protocol is used */ if (unpack_hepv2(buf, len, &h)) { LM_ERR("hep unpacking failed\n"); return -1; } /* run hep callbacks if looks like non-SIP message*/ if( !isalpha(msg.s[0]) ) { /* not-SIP related */ ret=run_hep_cbs(&h, &ri); if (ret < 0) { LM_ERR("failed to run hep callbacks\n"); return -1; } /* remove the hep header; leave only the payload */ memmove(buf, h.u.hepv12.payload, /* also copy '\0' character */ strlen(h.u.hepv12.payload)+1); msg.s = buf; msg.len = strlen(buf); } if (ri.src_port==0){ tmp=ip_addr2a(&ri.src_ip); LM_INFO("dropping 0 port packet for %s\n", tmp); return 0; } if (ret != HEP_SCRIPT_SKIP) { /* receive_msg must free buf too!*/ receive_msg( msg.s, msg.len, &ri); } return 0; }
int mi_init_datagram_server(sockaddr_dtgram *addr, unsigned int socket_domain, rx_tx_sockets * socks, int mode, int uid, int gid ) { char * socket_name; /* create sockets rx and tx ... */ /***********************************/ mi_socket_domain = socket_domain; /**********************************/ socks->rx_sock = socket(socket_domain, SOCK_DGRAM, 0); if (socks->rx_sock == -1) { LM_ERR("cannot create RX socket: %s\n", strerror(errno)); return -1; } switch(socket_domain) { case AF_LOCAL: LM_DBG("we have a unix socket: %s\n", addr->unix_addr.sun_path); socket_name = addr->unix_addr.sun_path; if(bind(socks->rx_sock,(struct sockaddr*)&addr->unix_addr, SUN_LEN(&addr->unix_addr))< 0) { LM_ERR("bind: %s\n", strerror(errno)); goto err_rx; } if(mi_sock_check(socks->rx_sock, socket_name)!=0) goto err_rx; /* change permissions */ if (mode){ if (chmod(socket_name, mode)<0){ LM_ERR("failed to change the permissions for %s to %04o:" "%s[%d]\n",socket_name, mode, strerror(errno), errno); goto err_rx; } } /* change ownership */ if ((uid!=-1) || (gid!=-1)){ if (chown(socket_name, uid, gid)<0){ LM_ERR("failed to change the owner/group for %s to %d.%d;" "%s[%d]\n",socket_name, uid, gid, strerror(errno), errno); goto err_rx; } } /* create TX socket */ socks->tx_sock = socket( socket_domain, SOCK_DGRAM, 0); if (socks->tx_sock == -1) { LM_ERR("cannot create socket: %s\n", strerror(errno)); goto err_rx; }; /* Turn non-blocking mode on for tx*/ flags = fcntl(socks->tx_sock, F_GETFL); if (flags == -1) { LM_ERR("fcntl failed: %s\n", strerror(errno)); goto err_both; } if (fcntl(socks->tx_sock, F_SETFL, flags | O_NONBLOCK) == -1) { LM_ERR("fcntl: set non-blocking failed: %s\n",strerror(errno)); goto err_both; } break; case AF_INET: if (bind(socks->rx_sock, &addr->udp_addr.s, sockaddru_len(addr->udp_addr))< 0) { LM_ERR("bind: %s\n", strerror(errno)); goto err_rx; } socks->tx_sock = socks->rx_sock; break; #ifdef USE_IPV6 case AF_INET6: if(bind(socks->rx_sock, (struct sockaddr*)&addr->udp_addr.sin6, sizeof(addr->udp_addr)) < 0) { LM_ERR("bind: %s\n", strerror(errno)); goto err_rx; } socks->tx_sock = socks->rx_sock; break; #endif default: LM_ERR("domain not supported\n"); goto err_rx; } return 0; err_both: close(socks->tx_sock); err_rx: close(socks->rx_sock); return -1; }
/** send an udp packet over an IP_HDRINCL raw socket. * If needed, send several fragments. * @param rsock - raw socket * @param buf - data * @param len - data len * @param from - source address:port (_must_ be non-null, but the ip address * can be 0, in which case it will be filled by the kernel). * @param to - destination address:port * @param mtu - maximum datagram size (including the ip header, excluding * link layer headers). Minimum allowed size is 28 * (sizeof(ip_header + udp_header)). If mtu is lower, it will * be ignored (the packet will be sent un-fragmented). * 0 can be used to disable fragmentation. * @return <0 on error (-2: datagram too big, -1: check errno), * number of bytes sent on success * (including the ip & udp headers => * on success len + udpheader + ipheader size). */ int raw_iphdr_udp4_send(int rsock, char* buf, unsigned int len, union sockaddr_union* from, union sockaddr_union* to, unsigned short mtu) { struct msghdr snd_msg; struct iovec iov[2]; struct ip_udp_hdr { struct ip ip; struct udphdr udp; } hdr; unsigned int totlen; #ifndef RAW_IPHDR_INC_AUTO_FRAG unsigned int ip_frag_size; /* fragment size */ unsigned int last_frag_extra; /* extra bytes possible in the last frag */ unsigned int ip_payload; unsigned int last_frag_offs; void* last_frag_start; int frg_no; #endif /* RAW_IPHDR_INC_AUTO_FRAG */ int ret; totlen = len + sizeof(hdr); if (unlikely(totlen) > 65535) return -2; memset(&snd_msg, 0, sizeof(snd_msg)); snd_msg.msg_name=&to->sin; snd_msg.msg_namelen=sockaddru_len(*to); snd_msg.msg_iov=&iov[0]; /* prepare the udp & ip headers */ mk_udp_hdr(&hdr.udp, &from->sin, &to->sin, (unsigned char*)buf, len, 1); mk_ip_hdr(&hdr.ip, &from->sin.sin_addr, &to->sin.sin_addr, len + sizeof(hdr.udp), IPPROTO_UDP); iov[0].iov_base=(char*)&hdr; iov[0].iov_len=sizeof(hdr); snd_msg.msg_iovlen=2; snd_msg.msg_control=0; snd_msg.msg_controllen=0; snd_msg.msg_flags=0; /* this part changes for different fragments */ /* packets are fragmented if mtu has a valid value (at least an IP header + UDP header fit in it) and if the total length is greater then the mtu */ #ifndef RAW_IPHDR_INC_AUTO_FRAG if (likely(totlen <= mtu || mtu <= sizeof(hdr))) { #endif /* RAW_IPHDR_INC_AUTO_FRAG */ iov[1].iov_base=buf; iov[1].iov_len=len; ret=sendmsg(rsock, &snd_msg, 0); #ifndef RAW_IPHDR_INC_AUTO_FRAG } else { ip_payload = len + sizeof(hdr.udp); /* a fragment offset must be a multiple of 8 => its size must also be a multiple of 8, except for the last fragment */ ip_frag_size = (mtu -sizeof(hdr.ip)) & (~7); last_frag_extra = (mtu - sizeof(hdr.ip)) & 7; /* rest */ frg_no = ip_payload / ip_frag_size + ((ip_payload % ip_frag_size) > last_frag_extra); /*ip_last_frag_size = ip_payload % frag_size + ((ip_payload % frag_size) <= last_frag_extra) * ip_frag_size; */ last_frag_offs = (frg_no - 1) * ip_frag_size; /* if we are here mtu => sizeof(ip_h+udp_h) && payload > mtu => last_frag_offs >= sizeof(hdr.udp) */ last_frag_start = buf + last_frag_offs - sizeof(hdr.udp); hdr.ip.ip_id = fastrand_max(65534) + 1; /* random id, should be != 0 (if 0 the kernel will fill it) */ /* send the first fragment */ iov[1].iov_base=buf; /* ip_frag_size >= sizeof(hdr.udp) because we are here only if mtu >= sizeof(hdr.ip) + sizeof(hdr.udp) */ iov[1].iov_len=ip_frag_size - sizeof(hdr.udp); hdr.ip.ip_len = RAW_IPHDR_IP_LEN(ip_frag_size + sizeof(hdr.ip)); hdr.ip.ip_off = RAW_IPHDR_IP_OFF(0x2000); /* set MF */ ret=sendmsg(rsock, &snd_msg, 0); if (unlikely(ret < 0)) goto end; /* all the other fragments, include only the ip header */ iov[0].iov_len = sizeof(hdr.ip); iov[1].iov_base = (char*)iov[1].iov_base + iov[1].iov_len; /* fragments between the first and the last */ while(unlikely(iov[1].iov_base < last_frag_start)) { iov[1].iov_len = ip_frag_size; hdr.ip.ip_len = RAW_IPHDR_IP_LEN(iov[1].iov_len + sizeof(hdr.ip)); /* set MF */ hdr.ip.ip_off = RAW_IPHDR_IP_OFF( (unsigned short) (((char*)iov[1].iov_base - (char*)buf + sizeof(hdr.udp)) / 8) | 0x2000 ); ret=sendmsg(rsock, &snd_msg, 0); if (unlikely(ret < 0)) goto end; iov[1].iov_base = (char*)iov[1].iov_base + iov[1].iov_len; } /* last fragment */ iov[1].iov_len = buf + len - (char*)iov[1].iov_base; hdr.ip.ip_len = RAW_IPHDR_IP_LEN(iov[1].iov_len + sizeof(hdr.ip)); /* don't set MF (last fragment) */ hdr.ip.ip_off = RAW_IPHDR_IP_OFF((unsigned short) (((char*)iov[1].iov_base - (char*)buf + sizeof(hdr.udp)) / 8) ); ret=sendmsg(rsock, &snd_msg, 0); if (unlikely(ret < 0)) goto end; } end: #endif /* RAW_IPHDR_INC_AUTO_FRAG */ return ret; }
static int udp_read_req(struct socket_info *si, int* bytes_read) { struct receive_info ri; int len; #ifdef DYN_BUF char* buf; #else static char buf [BUF_SIZE+1]; #endif char *tmp; unsigned int fromlen; callback_list* p; str msg; #ifdef DYN_BUF buf=pkg_malloc(BUF_SIZE+1); if (buf==0){ LM_ERR("could not allocate receive buffer\n"); goto error; } #endif fromlen=sockaddru_len(si->su); len=recvfrom(bind_address->socket, buf, BUF_SIZE,0,&ri.src_su.s,&fromlen); if (len==-1){ if (errno==EAGAIN) return 0; if ((errno==EINTR)||(errno==EWOULDBLOCK)|| (errno==ECONNREFUSED)) return -1; LM_ERR("recvfrom:[%d] %s\n", errno, strerror(errno)); return -2; } if (len<MIN_UDP_PACKET) { LM_DBG("probing packet received len = %d\n", len); return 0; } /* we must 0-term the messages, receive_msg expects it */ buf[len]=0; /* no need to save the previous char */ ri.bind_address = si; ri.dst_port = si->port_no; ri.dst_ip = si->address; ri.proto = si->proto; ri.proto_reserved1 = ri.proto_reserved2 = 0; su2ip_addr(&ri.src_ip, &ri.src_su); ri.src_port=su_getport(&ri.src_su); msg.s = buf; msg.len = len; /* run callbacks if looks like non-SIP message*/ if( !isalpha(msg.s[0]) ){ /* not-SIP related */ for(p = cb_list; p; p = p->next){ if(p->b == msg.s[1]){ if (p->func(bind_address->socket, &ri, &msg, p->param)==0){ /* buffer consumed by callback */ break; } } } if (p) return 0; } if (ri.src_port==0){ tmp=ip_addr2a(&ri.src_ip); LM_INFO("dropping 0 port packet from %s\n", tmp); return 0; } /* receive_msg must free buf too!*/ receive_msg( msg.s, msg.len, &ri); return 0; }
int sctp_server_init(struct socket_info* sock_info) { union sockaddr_union* addr; int optval; addr=&sock_info->su; sock_info->proto=PROTO_SCTP; if (init_su(addr, &sock_info->address, sock_info->port_no)<0){ LM_ERR("could not init sockaddr_union\n"); goto error; } sock_info->socket = socket(AF2PF(addr->s.sa_family), SOCK_SEQPACKET, 0); if (sock_info->socket==-1){ LM_ERR("socket: %s [%d]\n", strerror(errno), errno); goto error; } optval=1; if (setsockopt(sock_info->socket, SOL_SOCKET, SO_REUSEADDR , (void*)&optval, sizeof(optval)) ==-1){ LM_ERR("setsockopt: %s\n", strerror(errno)); goto error; } #ifdef DISABLE_NAGLE /* turns of Nagle-like algorithm/chunk-bundling.*/ optval=1; if (setsockopt(sock_info->socket, IPPROTO_SCTP, SCTP_NODELAY, (void*)&optval, sizeof(optval))==-1){ LM_WARN("setsockopt %s\n", strerror(errno)); /* continues since this is not critical */ } #endif /* tos */ /* this sockopt causes a kernel panic in some sctp implementations. * commenting it out. -gmarmon */ /* optval=tos; if (setsockopt(sock_info->socket, IPPROTO_IP, IP_TOS, (void*)&optval, sizeof(optval)) ==-1){ LM_WARN("setsockopt tos: %s\n", strerror(errno)); } */ #if defined (__linux__) && defined(SCTP_ERRORS) /* will SCTP_ERRORS ever be defined? -gmarmon */ optval=1; /* enable error receiving on unconnected sockets */ if(setsockopt(sock_info->socket, SOL_IP, IP_RECVERR, (void*)&optval, sizeof(optval)) ==-1){ LM_ERR("setsockopt: %s\n", strerror(errno)); goto error; } #endif /*if ( probe_max_receive_buffer(sock_info->socket)==-1) goto error; */ if (bind(sock_info->socket, &addr->s, sockaddru_len(*addr))==-1){ LM_ERR("bind(%x, %p, %d) on %s: %s\n", sock_info->socket, &addr->s, (unsigned)sockaddru_len(*addr), sock_info->address_str.s, strerror(errno)); #ifdef USE_IPV6 if (addr->s.sa_family==AF_INET6) LM_ERR("might be caused by using a link " " local address, try site local or global\n"); #endif goto error; } if(listen(sock_info->socket, LISTEN_BACKLOG)<0){ LM_ERR("listen(%x, %d) on %s: %s\n", sock_info->socket, LISTEN_BACKLOG, sock_info->address_str.s, strerror(errno)); goto error; } /* pkg_free(addr);*/ return 0; error: /* if (addr) pkg_free(addr);*/ return -1; }
int sctp_server_rcv_loop() { int len; #ifdef DYN_BUF char* buf; #else static char buf [BUF_SIZE+1]; #endif char *tmp; union sockaddr_union* from; unsigned int fromlen; struct receive_info ri; struct sctp_sndrcvinfo sinfo; from=(union sockaddr_union*) pkg_malloc(sizeof(union sockaddr_union)); if (from==0){ LM_ERR("out of pkg memory\n"); goto error; } memset(&sinfo, 0 , sizeof(sinfo)); ri.bind_address=bind_address; /* this will not change, we do it only once*/ ri.dst_port=bind_address->port_no; ri.dst_ip=bind_address->address; ri.proto=PROTO_SCTP; ri.proto_reserved1=ri.proto_reserved2=0; for(;;){ #ifdef DYN_BUF buf=pkg_malloc(BUF_SIZE+1); if (buf==0){ LM_ERR(" could not allocate receive buffer in pkg memory\n"); goto error; } #endif fromlen=sockaddru_len(bind_address->su); len = sctp_recvmsg(bind_address->socket, buf, BUF_SIZE, &from->s, &fromlen, &sinfo, 0); if (len==-1){ if (errno==EAGAIN){ LM_DBG("packet with bad checksum received\n"); continue; } LM_ERR("sctp_recvmsg:[%d] %s\n", errno, strerror(errno)); if ((errno==EINTR)||(errno==EWOULDBLOCK)|| (errno==ECONNREFUSED)) continue; /* goto skip;*/ else goto error; } /* we must 0-term the messages, receive_msg expects it */ buf[len]=0; /* no need to save the previous char */ ri.src_su=*from; su2ip_addr(&ri.src_ip, from); ri.src_port=su_getport(from); #ifndef NO_ZERO_CHECKS if (buf[len-1]==0) { tmp=ip_addr2a(&ri.src_ip); LM_WARN("upstream bug - 0-terminated packet from %s %d\n", tmp, htons(ri.src_port)); len--; } #endif if (ri.src_port==0){ tmp=ip_addr2a(&ri.src_ip); LM_INFO("dropping 0 port packet from %s\n", tmp); continue; } /* receive_msg must free buf too!*/ receive_msg(buf, len, &ri); /* skip: do other stuff */ } /* if (from) pkg_free(from); return 0; */ error: if (from) pkg_free(from); return -1; }
struct socket_info* get_out_socket(union sockaddr_union* to, int proto) { int* temp_sock; socklen_t len; union sockaddr_union from; struct socket_info* si; struct ip_addr ip; union sockaddr_union uncon; memset(&uncon, 0, sizeof(union sockaddr_union)); uncon.sin.sin_family = AF_UNSPEC; if (unlikely(proto!=PROTO_UDP)) { LOG(L_CRIT, "BUG: get_out_socket can only be called for UDP\n"); return 0; } retry: switch(to->s.sa_family){ case AF_INET : { if(unlikely(sock_inet < 0)){ sock_inet = socket(AF_INET, SOCK_DGRAM, 0); if (sock_inet==-1) { LM_ERR("socket() failed: %s\n", strerror(errno)); return 0; } } temp_sock = &sock_inet; break; } #ifdef USE_IPV6 case AF_INET6 : { if(unlikely(sock_inet6 < 0)){ sock_inet6 = socket(AF_INET6, SOCK_DGRAM, 0); if (sock_inet6==-1) { LM_ERR("socket() failed: %s\n", strerror(errno)); return 0; } } temp_sock = &sock_inet6; break; } #endif /* USE_IPV6 */ default: { LM_ERR("Unknown protocol family \n"); return 0; } } if( !mhomed_sock_cache_disabled ){ /* some Linux kernel versions (all?) along with other UNIXes don't re-bound the sock if already bound */ /* to un-bound a socket set sin_family to AF_UNSPEC and zero out the rest*/ if (unlikely(connect(*temp_sock, &uncon.s, sockaddru_len(uncon)) < 0)) mhomed_sock_cache_disabled = 1; } if (unlikely(connect(*temp_sock, &to->s, sockaddru_len(*to))==-1)) { if (unlikely(errno==EISCONN && !mhomed_sock_cache_disabled)){ /* no multiple connects support on the same socket */ mhomed_sock_cache_disabled=1; if (sock_inet>=0){ close(sock_inet); sock_inet=-1; } #ifdef USE_IPV6 if (sock_inet6>=0){ close(sock_inet6); sock_inet6=-1; } #endif /* USE_IPV6 */ goto retry; } LOG(L_ERR, "ERROR: get_out_socket: connect failed: %s\n", strerror(errno)); goto error; } len=sizeof(from); if (unlikely(getsockname(*temp_sock, &from.s, &len)==-1)) { LOG(L_ERR, "ERROR: get_out_socket: getsockname failed: %s\n", strerror(errno)); goto error; } su2ip_addr(&ip, &from); si=find_si(&ip, 0, proto); if (si==0) goto error; DBG("DEBUG: get_out_socket: socket determined: %p\n", si ); if (unlikely(mhomed_sock_cache_disabled)){ close(*temp_sock); *temp_sock=-1; } return si; error: LOG(L_ERR, "ERROR: get_out_socket: no socket found\n"); if (unlikely(mhomed_sock_cache_disabled && *temp_sock >=0)){ close(*temp_sock); *temp_sock=-1; } return 0; }
int jsonrpc_dgram_init_server(jsonrpc_dgram_sockaddr_t *addr, unsigned int socket_domain, jsonrpc_dgram_rx_tx_t *socks, int mode, int uid, int gid ) { char *socket_name; int flags; int optval; /* create sockets for rx and tx ... */ jsonrpc_dgram_socket_domain = socket_domain; socks->rx_sock = socket(socket_domain, SOCK_DGRAM, 0); if (socks->rx_sock == -1) { LM_ERR("cannot create RX socket: %s\n", strerror(errno)); return -1; } switch(socket_domain) { case AF_LOCAL: LM_DBG("we have a unix socket: %s\n", addr->unix_addr.sun_path); socket_name = addr->unix_addr.sun_path; if(bind(socks->rx_sock,(struct sockaddr*)&addr->unix_addr, SUN_LEN(&addr->unix_addr))< 0) { LM_ERR("bind: %s\n", strerror(errno)); goto err_rx; } if(jsonrpc_dgram_sock_check(socks->rx_sock, socket_name)!=0) goto err_rx; /* change permissions */ if (mode){ if (chmod(socket_name, mode)<0){ LM_ERR("failed to change the permissions for %s to %04o:" "%s[%d]\n",socket_name, mode, strerror(errno), errno); goto err_rx; } } /* change ownership */ if ((uid!=-1) || (gid!=-1)){ if (chown(socket_name, uid, gid)<0){ LM_ERR("failed to change the owner/group for %s to %d.%d;" "%s[%d]\n", socket_name, uid, gid, strerror(errno), errno); goto err_rx; } } break; case AF_INET: if (bind(socks->rx_sock, &addr->udp_addr.s, sockaddru_len(addr->udp_addr))< 0) { LM_ERR("bind: %s\n", strerror(errno)); goto err_rx; } break; case AF_INET6: if(bind(socks->rx_sock, (struct sockaddr*)&addr->udp_addr.sin6, sizeof(addr->udp_addr)) < 0) { LM_ERR("bind: %s\n", strerror(errno)); goto err_rx; } break; default: LM_ERR("domain not supported\n"); goto err_both; } jsonrpc_dgram_create_reply_socket(socks->tx_sock, socket_domain, err_both); optval = 64 * 1024; if (setsockopt(socks->tx_sock, SOL_SOCKET, SO_SNDBUF, (void*)&optval, sizeof(optval)) ==-1){ LM_ERR("failed to increse send buffer size via setsockopt " " SO_SNDBUF (%d) - %d: %s\n", optval, errno, strerror(errno)); /* continue, non-critical */ } return 0; err_both: close(socks->tx_sock); err_rx: close(socks->rx_sock); return -1; }
/* opens, binds and listens-on a control tcp socket * returns socket fd or -1 on error */ int init_tcpudp_sock(union sockaddr_union* sa_un, char* address, int port, enum socket_protos type) { union sockaddr_union su; struct hostent* he; int s; int optval; s=-1; if ((type!=UDP_SOCK) && (type!=TCP_SOCK)){ LOG(L_CRIT, "BUG: init_tcpudp_sock called with bad type: %d\n", type); goto error; } memset(&su, 0, sizeof (su)); /* if no address specified, or address=='*', listen on all * ipv4 addresses */ if ((address==0)||((*address)==0)||((*address=='*') && (*(address+1)==0))){ su.sin.sin_family=AF_INET; su.sin.sin_port=htons(port); su.sin.sin_addr.s_addr=INADDR_ANY; #ifdef HAVE_SOCKADDR_SA_LEN su.sin.sin_len=sizeof(struct sockaddr_in); #endif }else{ he=resolvehost(address); if (he==0){ LOG(L_ERR, "ERROR: init_tcpudp_sock: bad address %s\n", address); goto error; } if (hostent2su(&su, he, 0, port)==-1) goto error; } s=socket(AF2PF(su.s.sa_family), (type==TCP_SOCK)?SOCK_STREAM:SOCK_DGRAM,0); if (s==-1){ LOG(L_ERR, "ERROR: init_tcpudp_sock: cannot create tcp socket:" " %s [%d]\n", strerror(errno), errno); goto error; } /* REUSEADDR */ optval=1; if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval))==-1){ LOG(L_ERR, "ERROR: init_tcpudp_sock: setsockopt: %s [%d]\n", strerror(errno), errno); /* continue */ } /* tos */ optval=IPTOS_LOWDELAY; if (setsockopt(s, IPPROTO_IP, IP_TOS, (void*)&optval,sizeof(optval)) ==-1){ LOG(L_WARN, "WARNING: init_tcpudp_sock: setsockopt tos: %s\n", strerror(errno)); /* continue since this is not critical */ } if (set_non_blocking(s)==-1){ LOG(L_ERR, "ERROR: init_tcpudp_sock: set non blocking failed\n"); } if (bind(s, &su.s, sockaddru_len(su))==-1){ LOG(L_ERR, "ERROR: init_tcpudp_sock: bind: %s [%d]\n", strerror(errno), errno); goto error; } if ((type==TCP_SOCK) && (listen(s, 128)==-1)){ LOG(L_ERR, "ERROR: init_tcpudp_sock: listen: %s [%d]\n", strerror(errno), errno); goto error; } *sa_un=su; return s; error: if (s!=-1) close(s); return -1; }
static int tcpconn_async_connect(struct socket_info* send_sock, union sockaddr_union* server, char *buf, unsigned len, struct tcp_connection** c, int *ret_fd) { int fd, n; union sockaddr_union my_name; socklen_t my_name_len; struct tcp_connection* con; struct pollfd pf; unsigned int elapsed,to; int err; unsigned int err_len; int poll_err; char *ip; unsigned short port; struct timeval begin; /* create the socket */ fd = socket(AF2PF(server->s.sa_family), SOCK_STREAM, 0); if (fd == -1){ LM_ERR("socket: (%d) %s\n", errno, strerror(errno)); return -1; } if (tcp_init_sock_opt(fd)<0){ LM_ERR("tcp_init_sock_opt failed\n"); goto error; } my_name_len = sockaddru_len(send_sock->su); memcpy( &my_name, &send_sock->su, my_name_len); su_setport( &my_name, 0); if (bind(fd, &my_name.s, my_name_len )!=0) { LM_ERR("bind failed (%d) %s\n", errno,strerror(errno)); goto error; } /* attempt to do connect and see if we do block or not */ poll_err = 0; elapsed = 0; to = hep_async_local_connect_timeout*1000; if (gettimeofday(&(begin), NULL)) { LM_ERR("Failed to get TCP connect start time\n"); goto error; } again: n = connect(fd, &server->s, sockaddru_len(*server)); if (n == -1) { if (errno == EINTR){ elapsed=get_time_diff(&begin); if (elapsed < to) goto again; else { LM_DBG("Local connect attempt failed \n"); goto async_connect; } } if (errno != EINPROGRESS && errno!=EALREADY) { get_su_info(&server->s, ip, port); LM_ERR("[server=%s:%d] (%d) %s\n",ip, port, errno,strerror(errno)); goto error; } } else goto local_connect; /* let's poll for a little */ pf.fd = fd; pf.events = POLLOUT; while(1){ elapsed = get_time_diff(&begin); if (elapsed < to) to -= elapsed; else { LM_DBG("Polling is overdue \n"); goto async_connect; } n = poll(&pf, 1, to/1000); if (n < 0){ if (errno == EINTR) continue; get_su_info(&server->s, ip, port); LM_ERR("poll/select failed:[server=%s:%d] (%d) %s\n", ip, port, errno, strerror(errno)); goto error; } else if (n==0) /* timeout */ continue; if (pf.revents & (POLLERR|POLLHUP|POLLNVAL)){ LM_ERR("poll error: flags %x\n", pf.revents); poll_err=1; } err_len=sizeof(err); getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &err_len); if ((err==0) && (poll_err==0)) goto local_connect; if (err!=EINPROGRESS && err!=EALREADY){ get_su_info(&server->s, ip, port); LM_ERR("failed to retrieve SO_ERROR [server=%s:%d] (%d) %s\n", ip, port, err, strerror(err)); goto error; } } async_connect: LM_DBG("Create connection for async connect\n"); /* create a new dummy connection */ con = tcp_conn_create(fd, server, send_sock, S_CONN_CONNECTING); if (con==NULL) { LM_ERR("tcp_conn_create failed\n"); goto error; } /* attach the write buffer to it */ lock_get(&con->write_lock); if (add_write_chunk(con,buf,len,0) < 0) { LM_ERR("Failed to add the initial write chunk\n"); /* FIXME - seems no more SHM now ... * continue the async connect process ? */ } lock_release(&con->write_lock); /* report an async, in progress connect */ *c = con; return 0; local_connect: con = tcp_conn_create(fd, server, send_sock, S_CONN_OK); if (con==NULL) { LM_ERR("tcp_conn_create failed, closing the socket\n"); goto error; } *c = con; *ret_fd = fd; /* report a local connect */ return 1; error: close(fd); *c = NULL; return -1; }
/* initializes an already defined TCP listener */ int tcp_init_listener(struct socket_info *si) { union sockaddr_union* addr; int optval; #ifdef DISABLE_NAGLE int flag; struct protoent* pe; if (tcp_proto_no==-1){ /* if not already set */ pe=getprotobyname("tcp"); if (pe==0){ LM_ERR("could not get TCP protocol number\n"); tcp_proto_no=-1; }else{ tcp_proto_no=pe->p_proto; } } #endif addr = &si->su; if (init_su(addr, &si->address, si->port_no)<0){ LM_ERR("could no init sockaddr_union\n"); goto error; } si->socket = socket(AF2PF(addr->s.sa_family), SOCK_STREAM, 0); if (si->socket==-1){ LM_ERR("socket failed with [%s]\n", strerror(errno)); goto error; } #ifdef DISABLE_NAGLE flag=1; if ( (tcp_proto_no!=-1) && (setsockopt(si->socket, tcp_proto_no , TCP_NODELAY, &flag, sizeof(flag))<0) ){ LM_ERR("could not disable Nagle: %s\n",strerror(errno)); } #endif #if !defined(TCP_DONT_REUSEADDR) /* Stevens, "Network Programming", Section 7.5, "Generic Socket * Options": "...server started,..a child continues..on existing * connection..listening server is restarted...call to bind fails * ... ALL TCP servers should specify the SO_REUSEADDRE option * to allow the server to be restarted in this situation */ optval=1; if (setsockopt(si->socket, SOL_SOCKET, SO_REUSEADDR, (void*)&optval, sizeof(optval))==-1) { LM_ERR("setsockopt failed with [%s]\n", strerror(errno)); goto error; } #endif /* tos */ optval = tos; if (setsockopt(si->socket, IPPROTO_IP, IP_TOS, (void*)&optval, sizeof(optval)) ==-1){ LM_WARN("setsockopt tos: %s\n", strerror(errno)); /* continue since this is not critical */ } if (probe_max_sock_buff(si->socket,1,MAX_SEND_BUFFER_SIZE, BUFFER_INCREMENT)) { LM_WARN("setsockopt tcp snd buff: %s\n",strerror(errno)); /* continue since this is not critical */ } init_sock_keepalive(si->socket); if (bind(si->socket, &addr->s, sockaddru_len(*addr))==-1){ LM_ERR("bind(%x, %p, %d) on %s:%d : %s\n", si->socket, &addr->s, (unsigned)sockaddru_len(*addr), si->address_str.s, si->port_no, strerror(errno)); goto error; } if (listen(si->socket, tcp_listen_backlog)==-1){ LM_ERR("listen(%x, %p, %d) on %s: %s\n", si->socket, &addr->s, (unsigned)sockaddru_len(*addr), si->address_str.s, strerror(errno)); goto error; } return 0; error: if (si->socket!=-1){ close(si->socket); si->socket=-1; } return -1; }
static int hep_udp_read_req(struct socket_info *si, int* bytes_read) { struct receive_info ri; int len; #ifdef DYN_BUF char* buf; #else static char buf [BUF_SIZE+1]; #endif char *tmp; unsigned int fromlen; str msg; struct hep_context *hep_ctx; int ret = 0; context_p ctx=NULL; #ifdef DYN_BUF buf=pkg_malloc(BUF_SIZE+1); if (buf==0){ LM_ERR("could not allocate receive buffer\n"); goto error; } #endif fromlen=sockaddru_len(si->su); len=recvfrom(bind_address->socket, buf, BUF_SIZE,0,&ri.src_su.s,&fromlen); if (len==-1){ if (errno==EAGAIN) return 0; if ((errno==EINTR)||(errno==EWOULDBLOCK)|| (errno==ECONNREFUSED)) return -1; LM_ERR("recvfrom:[%d] %s\n", errno, strerror(errno)); return -2; } if (len<MIN_UDP_PACKET) { LM_DBG("probing packet received len = %d\n", len); return 0; } /* we must 0-term the messages, receive_msg expects it */ buf[len]=0; /* no need to save the previous char */ ri.bind_address = si; ri.dst_port = si->port_no; ri.dst_ip = si->address; ri.proto = si->proto; ri.proto_reserved1 = ri.proto_reserved2 = 0; su2ip_addr(&ri.src_ip, &ri.src_su); ri.src_port=su_getport(&ri.src_su); /* if udp we are sure that version 1 or 2 of the * protocol is used */ if ((hep_ctx = shm_malloc(sizeof(struct hep_context))) == NULL) { LM_ERR("no more shared memory!\n"); return -1; } memset(hep_ctx, 0, sizeof(struct hep_context)); memcpy(&hep_ctx->ri, &ri, sizeof(struct receive_info)); if (len < 4) { LM_ERR("invalid message! too short!\n"); return -1; } if (!memcmp(buf, HEP_HEADER_ID, HEP_HEADER_ID_LEN)) { /* HEPv3 */ if (unpack_hepv3(buf, len, &hep_ctx->h)) { LM_ERR("hepv3 unpacking failed\n"); return -1; } } else { /* HEPv2 */ if (unpack_hepv12(buf, len, &hep_ctx->h)) { LM_ERR("hepv12 unpacking failed\n"); return -1; } } /* set context for receive_msg */ if ((ctx=context_alloc(CONTEXT_GLOBAL)) == NULL) { LM_ERR("failed to allocate new context! skipping...\n"); goto error_free_hep; } memset(ctx, 0, context_size(CONTEXT_GLOBAL)); context_put_ptr(CONTEXT_GLOBAL, ctx, hep_ctx_idx, hep_ctx); update_recv_info(&ri, &hep_ctx->h); /* run hep callbacks; set the current processing context * to hep context; this way callbacks will have all the data * needed */ current_processing_ctx = ctx; ret=run_hep_cbs(); if (ret < 0) { LM_ERR("failed to run hep callbacks\n"); return -1; } current_processing_ctx = NULL; if (hep_ctx->h.version == 3) { /* HEPv3 */ msg.len = hep_ctx->h.u.hepv3.payload_chunk.chunk.length- sizeof(hep_chunk_t); msg.s = hep_ctx->h.u.hepv3.payload_chunk.data; } else { /* HEPv12 */ msg.len = len - hep_ctx->h.u.hepv12.hdr.hp_l; msg.s = buf + hep_ctx->h.u.hepv12.hdr.hp_l; if (hep_ctx->h.u.hepv12.hdr.hp_v == 2) { msg.s += sizeof(struct hep_timehdr); msg.len -= sizeof(struct hep_timehdr); } } if (ri.src_port==0){ tmp=ip_addr2a(&ri.src_ip); LM_INFO("dropping 0 port packet for %s\n", tmp); return 0; } if (ret != HEP_SCRIPT_SKIP) { /* receive_msg must free buf too!*/ receive_msg( msg.s, msg.len, &ri, ctx); } return 0; error_free_hep: shm_free(hep_ctx); return -1; }
int tcp_init(struct socket_info* sock_info) { union sockaddr_union* addr; int optval; #ifdef DISABLE_NAGLE int flag; struct protoent* pe; if (tcp_proto_no==-1){ /* if not already set */ pe=getprotobyname("tcp"); if (pe==0){ LOG(L_ERR, "ERROR: tcp_init: could not get TCP protocol number\n"); tcp_proto_no=-1; }else{ tcp_proto_no=pe->p_proto; } } #endif addr=&sock_info->su; sock_info->proto=PROTO_TCP; if (init_su(addr, &sock_info->address, sock_info->port_no)<0){ LOG(L_ERR, "ERROR: tcp_init: could no init sockaddr_union\n"); goto error; } sock_info->socket=socket(AF2PF(addr->s.sa_family), SOCK_STREAM, 0); if (sock_info->socket==-1){ LOG(L_ERR, "ERROR: tcp_init: socket: %s\n", strerror(errno)); goto error; } #ifdef DISABLE_NAGLE flag=1; if ( (tcp_proto_no!=-1) && (setsockopt(sock_info->socket, tcp_proto_no , TCP_NODELAY, &flag, sizeof(flag))<0) ){ LOG(L_ERR, "ERROR: tcp_init: could not disable Nagle: %s\n", strerror(errno)); } #endif #if !defined(TCP_DONT_REUSEADDR) /* Stevens, "Network Programming", Section 7.5, "Generic Socket * Options": "...server started,..a child continues..on existing * connection..listening server is restarted...call to bind fails * ... ALL TCP servers should specify the SO_REUSEADDRE option * to allow the server to be restarted in this situation * * Indeed, without this option, the server can't restart. * -jiri */ optval=1; if (setsockopt(sock_info->socket, SOL_SOCKET, SO_REUSEADDR, (void*)&optval, sizeof(optval))==-1) { LOG(L_ERR, "ERROR: tcp_init: setsockopt %s\n", strerror(errno)); goto error; } #endif /* tos */ optval=IPTOS_LOWDELAY; if (setsockopt(sock_info->socket, IPPROTO_IP, IP_TOS, (void*)&optval, sizeof(optval)) ==-1){ LOG(L_WARN, "WARNING: tcp_init: setsockopt tos: %s\n", strerror(errno)); /* continue since this is not critical */ } if (bind(sock_info->socket, &addr->s, sockaddru_len(*addr))==-1){ LOG(L_ERR, "ERROR: tcp_init: bind(%x, %p, %d) on %s: %s\n", sock_info->socket, &addr->s, sockaddru_len(*addr), sock_info->address_str.s, strerror(errno)); goto error; } if (listen(sock_info->socket, 10)==-1){ LOG(L_ERR, "ERROR: tcp_init: listen(%x, %p, %d) on %s: %s\n", sock_info->socket, &addr->s, sockaddru_len(*addr), sock_info->address_str.s, strerror(errno)); goto error; } return 0; error: if (sock_info->socket!=-1){ close(sock_info->socket); sock_info->socket=-1; } return -1; }
/** create and return a raw socket. * @param proto - protocol used (e.g. IPPROTO_UDP, IPPROTO_RAW) * @param ip - if not null the socket will be bound on this ip. * @param iface - if not null the socket will be bound to this interface * (SO_BINDTODEVICE). This is supported only on linux. * @param iphdr_incl - set to 1 if packets send on this socket include * a pre-built ip header (some fields, like the checksum * will still be filled by the kernel, OTOH packet * fragmentation has to be done in user space). * @return socket on success, -1 on error */ int raw_socket(int proto, struct ip_addr* ip, str* iface, int iphdr_incl) { int sock; int t; union sockaddr_union su; #if defined (SO_BINDTODEVICE) char short_ifname[sizeof(int)]; int ifname_len; char* ifname; #endif /* SO_BINDTODEVICE */ sock = socket(PF_INET, SOCK_RAW, proto); if (sock==-1) goto error; /* set socket options */ if (iphdr_incl) { t=1; if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, &t, sizeof(t))<0){ ERR("raw_socket: setsockopt(IP_HDRINCL) failed: %s [%d]\n", strerror(errno), errno); goto error; } } else { /* IP_PKTINFO makes no sense if the ip header is included */ /* using IP_PKTINFO */ t=1; #ifdef IP_PKTINFO if (setsockopt(sock, IPPROTO_IP, IP_PKTINFO, &t, sizeof(t))<0){ ERR("raw_socket: setsockopt(IP_PKTINFO) failed: %s [%d]\n", strerror(errno), errno); goto error; } #elif defined(IP_RECVDSTADDR) if (setsockopt(sock, IPPROTO_IP, IP_RECVDSTADDR, &t, sizeof(t))<0){ ERR("raw_socket: setsockop(IP_RECVDSTADDR) failed: %s [%d]\n", strerror(errno), errno); goto error; } #else #error "no method of getting the destination ip address supported" #endif /* IP_RECVDSTADDR / IP_PKTINFO */ } #if defined (IP_MTU_DISCOVER) && defined (IP_PMTUDISC_DONT) t=IP_PMTUDISC_DONT; if(setsockopt(sock, IPPROTO_IP, IP_MTU_DISCOVER, &t, sizeof(t)) ==-1){ ERR("raw_socket: setsockopt(IP_MTU_DISCOVER): %s\n", strerror(errno)); goto error; } #endif /* IP_MTU_DISCOVER && IP_PMTUDISC_DONT */ if (iface && iface->s){ #if defined (SO_BINDTODEVICE) /* workaround for linux bug: arg to setsockopt must have at least * sizeof(int) size or EINVAL would be returned */ if (iface->len<sizeof(int)){ memcpy(short_ifname, iface->s, iface->len); short_ifname[iface->len]=0; /* make sure it's zero term */ ifname_len=sizeof(short_ifname); ifname=short_ifname; }else{ ifname_len=iface->len; ifname=iface->s; } if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, ifname, ifname_len) <0){ ERR("raw_socket: could not bind to %.*s: %s [%d]\n", iface->len, ZSW(iface->s), strerror(errno), errno); goto error; } #else /* !SO_BINDTODEVICE */ /* SO_BINDTODEVICE is linux specific => cannot bind to a device */ ERR("raw_socket: bind to device supported only on linux\n"); goto error; #endif /* SO_BINDTODEVICE */ } /* FIXME: probe_max_receive_buffer(sock) missing */ if (ip){ init_su(&su, ip, 0); if (bind(sock, &su.s, sockaddru_len(su))==-1){ ERR("raw_socket: bind(%s) failed: %s [%d]\n", ip_addr2a(ip), strerror(errno), errno); goto error; } } return sock; error: if (sock!=-1) close(sock); return -1; }