本文共 4180 字,大约阅读时间需要 13 分钟。
/* * recvfromto Like recvfrom, but also stores the destination * IP address. Useful on multihomed hosts. * * Should work on Linux and BSD. * * Copyright (C) 2002 Miquel van Smoorenburg. * * This program 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 of the License, or (at your option) any later version. */#include <sys/types.h>#include <sys/socket.h>#include <sys/uio.h>#include <netinet/in.h>#include <errno.h>#include <unistd.h>#include <fcntl.h>/* Remove this when autoconf can detect this. */#if defined(IP_PKTINFO) && !defined(HAVE_IP_PKTINFO)# define HAVE_IP_PKTINFO 1#elif defined(IP_RECVDSTADDR) && !defined(HAVE_IP_RECVDSTADDR)# define HAVE_IP_RECVDSTADDR#endifint recvfromto(int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen, struct sockaddr *to, socklen_t *tolen){ struct msghdr msgh; struct cmsghdr *cmsg; struct iovec iov; char cbuf[1024]; int opt, err; /* * If from or to are set, they must be big enough * to store a struct sockaddr_in. */ if ((from && (!fromlen || *fromlen < sizeof(struct sockaddr_in))) || (to && (!tolen || *tolen < sizeof(struct sockaddr_in)))) { errno = EINVAL; return -1; } if (tolen) *tolen = 0;#ifdef HAVE_IP_PKTINFO /* * IP_PKTINFO doesn't provide sin_port so we have to * retrieve it using getsockname(). */ if (to) { struct sockaddr_in si; socklen_t l = sizeof(si); ((struct sockaddr_in *)to)->sin_family = AF_INET; ((struct sockaddr_in *)to)->sin_port = 0; l = sizeof(si); if (getsockname(s, (struct sockaddr *)&si, &l) == 0) { ((struct sockaddr_in *)to)->sin_port = si.sin_port; ((struct sockaddr_in *)to)->sin_addr = si.sin_addr; } *tolen = sizeof(struct sockaddr_in); }#endif /* Set MSG_DONTWAIT if O_NONBLOCK was set. */ if (fcntl(s, F_GETFL) & O_NONBLOCK) flags |= MSG_DONTWAIT; /* Set up iov and msgh structures. */ iov.iov_base = buf; iov.iov_len = len; msgh.msg_control = cbuf; msgh.msg_controllen = sizeof(cbuf); msgh.msg_name = from; msgh.msg_namelen = fromlen ? *fromlen : 0; msgh.msg_iov = &iov; msgh.msg_iovlen = 1;#ifdef HAVE_IP_PKTINFO /* Set the IP_PKTINFO option (Linux). */ opt = 1; setsockopt(s, SOL_IP, IP_PKTINFO, &opt, sizeof(opt));#endif#ifdef HAVE_IP_RECVDSTADDR /* Set the IP_RECVDSTADDR option (BSD). */ opt = 1; setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt));#endif /* Receive one packet. */ err = recvmsg(s, &msgh, flags); if (fromlen) *fromlen = msgh.msg_namelen; /* Process auxiliary received data in msgh */ for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL && cmsg->cmsg_len >= sizeof(*cmsg); cmsg = CMSG_NXTHDR(&msgh,cmsg)) { #ifdef HAVE_IP_PKTINFO if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_PKTINFO) { struct in_pktinfo *i = (struct in_pktinfo *)CMSG_DATA(cmsg); if (to) { ((struct sockaddr_in *)to)->sin_addr = i->ipi_addr; *tolen = sizeof(struct sockaddr_in); } break; }#endif#ifdef HAVE_IP_RECVDSTADDR if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_RECVDSTADDR) { struct in_addr *i = (struct in_addr *)CMSG_DATA(cmsg); if (to) { ((struct sockaddr_in *)to)->sin_addr = *i; *tolen = sizeof(struct sockaddr_in); } }#endif } return err;}#ifdef STANDALONE#include <stdio.h>#include <stdlib.h>#include <arpa/inet.h>/* * Small test program to test recvfromto */int main(int argc, char **argv){ struct sockaddr_in from, to, in; char buf[1024]; int port = 20000; int n, s, fl, tl; if (argc > 1) port = atoi(argv[1]); s = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); in.sin_family = AF_INET; in.sin_port = htons(port); in.sin_addr.s_addr = INADDR_ANY; bind(s, &in, sizeof(in)); while (1) { fl = tl = sizeof(struct sockaddr_in); memset(&from, 0, sizeof(from)); memset(&to, 0, sizeof(to)); if ((n = recvfromto(s, buf, sizeof(buf), 0, (struct sockaddr *)&from, &fl, (struct sockaddr *)&to, &tl)) < 0) { perror("recvfromto"); break; } printf("Received a packet of %d bytes/n", n); printf(" src ip:port %s:%d/n", inet_ntoa(from.sin_addr), ntohs(from.sin_port)); printf(" dst ip:port %s:%d/n", inet_ntoa(to.sin_addr), ntohs(to.sin_port)); } return 0;}#endif /* STANDALONE */ 转载地址:http://stpbi.baihongyu.com/