File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / miniupnpd / minissdpd / asyncsendto.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Sep 27 11:25:11 2023 UTC (14 months, 4 weeks ago) by misho
Branches: miniupnpd, MAIN
CVS tags: v2_3_3p0, HEAD
Version 2.3.3p0

    1: /* $Id: asyncsendto.c,v 1.1.1.1 2023/09/27 11:25:11 misho Exp $ */
    2: /* MiniUPnP project
    3:  * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
    4:  * (c) 2006-2020 Thomas Bernard
    5:  * This software is subject to the conditions detailed
    6:  * in the LICENCE file provided within the distribution */
    7: 
    8: #include <sys/types.h>
    9: #include <sys/select.h>
   10: #include <sys/socket.h>
   11: #include <sys/time.h>
   12: #include <sys/queue.h>
   13: #include <stdlib.h>
   14: #include <string.h>
   15: #include <unistd.h>
   16: #include <syslog.h>
   17: #include <errno.h>
   18: #include <sys/uio.h>
   19: #include <netinet/in.h>
   20: #include <inttypes.h>
   21: 
   22: #include "asyncsendto.h"
   23: #include "upnputils.h"
   24: 
   25: enum send_state {ESCHEDULED=1, EWAITREADY=2, ESENDNOW=3} state;
   26: 
   27: /* state diagram for a packet :
   28:  *
   29:  *                     |
   30:  *                     V
   31:  * -> ESCHEDULED -> ESENDNOW -> sent
   32:  *                    ^  |
   33:  *                    |  V
   34:  *                EWAITREADY -> sent
   35:  */
   36: struct scheduled_send {
   37: 	LIST_ENTRY(scheduled_send) entries;
   38: 	struct timeval ts;
   39: 	enum send_state state;
   40: 	int sockfd;
   41: 	const void * buf;
   42: 	size_t len;
   43: 	int flags;
   44: 	const struct sockaddr *dest_addr;
   45: 	socklen_t addrlen;
   46: 	const struct sockaddr_in6 *src_addr;
   47: 	char data[];
   48: };
   49: 
   50: static LIST_HEAD(listhead, scheduled_send) send_list = { NULL };
   51: 
   52: /*
   53:  * ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
   54:  *                const struct sockaddr *dest_addr, socklen_t addrlen);
   55:  */
   56: static ssize_t
   57: send_from_to(int sockfd, const void *buf, size_t len, int flags,
   58:              const struct sockaddr_in6 *src_addr,
   59:              const struct sockaddr *dest_addr, socklen_t addrlen)
   60: {
   61: #ifdef IPV6_PKTINFO
   62: 	if(src_addr) {
   63: 		struct iovec iov;
   64: 		struct in6_pktinfo ipi6;
   65: 		uint8_t c[CMSG_SPACE(sizeof(ipi6))];
   66: 		struct msghdr msg;
   67: 		struct cmsghdr* cmsg;
   68: 
   69: 		iov.iov_base = (void *)buf;
   70: 		iov.iov_len = len;
   71: 		memset(&msg, 0, sizeof(msg));
   72: 		msg.msg_iov = &iov;
   73: 		msg.msg_iovlen = 1;
   74: 		ipi6.ipi6_addr = src_addr->sin6_addr;
   75: 		ipi6.ipi6_ifindex = src_addr->sin6_scope_id;
   76: 		msg.msg_control = c;
   77: 		msg.msg_controllen = sizeof(c);
   78: 		cmsg = CMSG_FIRSTHDR(&msg);
   79: 		cmsg->cmsg_level = IPPROTO_IPV6;
   80: 		cmsg->cmsg_type = IPV6_PKTINFO;
   81: 		cmsg->cmsg_len = CMSG_LEN(sizeof(ipi6));
   82: 		memcpy(CMSG_DATA(cmsg), &ipi6, sizeof(ipi6));
   83: 		msg.msg_name = (void *)dest_addr;
   84: 		msg.msg_namelen = addrlen;
   85: 		return sendmsg(sockfd, &msg, flags);
   86: 	} else {
   87: #endif /* IPV6_PKTINFO */
   88: 		return sendto(sockfd, buf, len, flags, dest_addr, addrlen);
   89: #ifdef IPV6_PKTINFO
   90: 	}
   91: #endif /* IPV6_PKTINFO */
   92: }
   93: 
   94: /* delay = milli seconds */
   95: ssize_t
   96: sendto_schedule2(int sockfd, const void *buf, size_t len, int flags,
   97:                  const struct sockaddr *dest_addr, socklen_t addrlen,
   98:                  const struct sockaddr_in6 *src_addr,
   99:                  unsigned int delay)
  100: {
  101: 	enum send_state state;
  102: 	ssize_t n;
  103: 	size_t alloc_len;
  104: 	struct timeval tv;
  105: 	struct scheduled_send * elt;
  106: 
  107: 	if(delay == 0) {
  108: 		/* first try to send at once */
  109: 		n = send_from_to(sockfd, buf, len, flags, src_addr, dest_addr, addrlen);
  110: 		if(n >= 0)
  111: 			return n;
  112: 		else if(errno == EAGAIN || errno == EWOULDBLOCK) {
  113: 			/* use select() on this socket */
  114: 			state = EWAITREADY;
  115: 		} else if(errno == EINTR) {
  116: 			state = ESENDNOW;
  117: 		} else {
  118: 			/* uncatched error */
  119: 			return n;
  120: 		}
  121: 	} else {
  122: 		state = ESCHEDULED;
  123: 	}
  124: 
  125: 	/* schedule */
  126: 	if(upnp_gettimeofday(&tv) < 0) {
  127: 		return -1;
  128: 	}
  129: 	/* allocate enough space for structure + buffers */
  130: 	alloc_len = sizeof(struct scheduled_send) + len + addrlen;
  131: 	if(src_addr)
  132: 		alloc_len += sizeof(struct sockaddr_in6);
  133: 	elt = malloc(alloc_len);
  134: 	if(elt == NULL) {
  135: 		syslog(LOG_ERR, "malloc failed to allocate %u bytes",
  136: 		       (unsigned)alloc_len);
  137: 		return -1;
  138: 	}
  139: 	elt->state = state;
  140: 	/* time the packet should be sent */
  141: 	elt->ts.tv_sec = tv.tv_sec + (delay / 1000);
  142: 	elt->ts.tv_usec = tv.tv_usec + (delay % 1000) * 1000;
  143: 	if(elt->ts.tv_usec > 1000000) {
  144: 		elt->ts.tv_sec++;
  145: 		elt->ts.tv_usec -= 1000000;
  146: 	}
  147: 	elt->sockfd = sockfd;
  148: 	elt->flags = flags;
  149: 	memcpy(elt->data, dest_addr, addrlen);
  150: 	elt->dest_addr = (struct sockaddr *)elt->data;
  151: 	elt->addrlen = addrlen;
  152: 	if(src_addr) {
  153: 		elt->src_addr = (struct sockaddr_in6 *)(elt->data + addrlen);
  154: 		memcpy((void *)elt->src_addr, src_addr, sizeof(struct sockaddr_in6));
  155: 		elt->buf = (void *)(elt->data + addrlen + sizeof(struct sockaddr_in6));
  156: 	} else {
  157: 		elt->src_addr = NULL;
  158: 		elt->buf = (void *)(elt->data + addrlen);
  159: 	}
  160: 	elt->len = len;
  161: 	memcpy((void *)elt->buf, buf, len);
  162: 	/* insert */
  163: 	LIST_INSERT_HEAD( &send_list, elt, entries);
  164: 	return 0;
  165: }
  166: 
  167: /* try to send at once, and queue the packet if needed */
  168: ssize_t
  169: sendto_or_schedule(int sockfd, const void *buf, size_t len, int flags,
  170:                    const struct sockaddr *dest_addr, socklen_t addrlen)
  171: {
  172: 	return sendto_schedule2(sockfd, buf, len, flags, dest_addr, addrlen, NULL, 0);
  173: }
  174: 
  175: ssize_t
  176: sendto_or_schedule2(int sockfd, const void *buf, size_t len, int flags,
  177:                    const struct sockaddr *dest_addr, socklen_t addrlen,
  178:                    const struct sockaddr_in6 *src_addr)
  179: {
  180: 	return sendto_schedule2(sockfd, buf, len, flags, dest_addr, addrlen, src_addr, 0);
  181: }
  182: 
  183: /* get_next_scheduled_send() return number of scheduled send in list */
  184: int get_next_scheduled_send(struct timeval * next_send)
  185: {
  186: 	int n = 0;
  187: 	struct scheduled_send * elt;
  188: 	if(next_send == NULL)
  189: 		return -1;
  190: 	for(elt = send_list.lh_first; elt != NULL; elt = elt->entries.le_next) {
  191: 		if(n == 0 || (elt->ts.tv_sec < next_send->tv_sec) ||
  192: 		   (elt->ts.tv_sec == next_send->tv_sec && elt->ts.tv_usec < next_send->tv_usec)) {
  193: 			next_send->tv_sec = elt->ts.tv_sec;
  194: 			next_send->tv_usec = elt->ts.tv_usec;
  195: 		}
  196: 		n++;
  197: 	}
  198: 	return n;
  199: }
  200: 
  201: /* update writefds for select() call
  202:  * return the number of packets to try to send at once */
  203: int get_sendto_fds(fd_set * writefds, int * max_fd, const struct timeval * now)
  204: {
  205: 	int n = 0;
  206: 	struct scheduled_send * elt;
  207: 	for(elt = send_list.lh_first; elt != NULL; elt = elt->entries.le_next) {
  208: 		if(elt->state == EWAITREADY) {
  209: 			/* last sendto() call returned EAGAIN/EWOULDBLOCK */
  210: 			FD_SET(elt->sockfd, writefds);
  211: 			if(elt->sockfd > *max_fd)
  212: 				*max_fd = elt->sockfd;
  213: 			n++;
  214: 		} else if((elt->ts.tv_sec < now->tv_sec) ||
  215: 		          (elt->ts.tv_sec == now->tv_sec && elt->ts.tv_usec <= now->tv_usec)) {
  216: 			/* we waited long enough, now send ! */
  217: 			elt->state = ESENDNOW;
  218: 			n++;
  219: 		}
  220: 	}
  221: 	return n;
  222: }
  223: 
  224: /* executed sendto() when needed */
  225: int try_sendto(fd_set * writefds)
  226: {
  227: 	int ret = 0;
  228: 	ssize_t n;
  229: 	struct scheduled_send * elt;
  230: 	struct scheduled_send * next;
  231: 	for(elt = send_list.lh_first; elt != NULL; elt = next) {
  232: 		next = elt->entries.le_next;
  233: 		if((elt->state == ESENDNOW) ||
  234: 		   (elt->state == EWAITREADY && FD_ISSET(elt->sockfd, writefds))) {
  235: #ifdef DEBUG
  236: 			syslog(LOG_DEBUG, "%s: %d bytes on socket %d",
  237: 			       "try_sendto", (int)elt->len, elt->sockfd);
  238: #endif
  239: 			n = send_from_to(elt->sockfd, elt->buf, elt->len, elt->flags,
  240: 			                 elt->src_addr, elt->dest_addr, elt->addrlen);
  241: 			/*n = sendto(elt->sockfd, elt->buf, elt->len, elt->flags,
  242: 			           elt->dest_addr, elt->addrlen);*/
  243: 			if(n < 0) {
  244: 				if(errno == EINTR) {
  245: 					/* retry at once */
  246: 					elt->state = ESENDNOW;
  247: 					continue;
  248: 				} else if(errno == EAGAIN || errno == EWOULDBLOCK) {
  249: 					/* retry once the socket is ready for writing */
  250: 					elt->state = EWAITREADY;
  251: 					continue;
  252: 				} else {
  253: 					char addr_str[64];
  254: 					/* uncatched error */
  255: 					if(sockaddr_to_string(elt->dest_addr, addr_str, sizeof(addr_str)) <= 0)
  256: 						addr_str[0] = '\0';
  257: 					syslog(LOG_ERR, "%s(sock=%d, len=%u, dest=%s): sendto: %m",
  258: 					       "try_sendto", elt->sockfd, (unsigned)elt->len,
  259: 					       addr_str);
  260: 					ret--;
  261: 				}
  262: 			} else if((int)n != (int)elt->len) {
  263: 				syslog(LOG_WARNING, "%s: %d bytes sent out of %d",
  264: 				       "try_sendto", (int)n, (int)elt->len);
  265: 			}
  266: 			/* remove from the list */
  267: 			LIST_REMOVE(elt, entries);
  268: 			free(elt);
  269: 		}
  270: 	}
  271: 	return ret;
  272: }
  273: 
  274: /* maximum execution time for finalize_sendto() in milliseconds */
  275: #define FINALIZE_SENDTO_DELAY	(500)
  276: 
  277: /* empty the list */
  278: void finalize_sendto(void)
  279: {
  280: 	ssize_t n;
  281: 	struct scheduled_send * elt;
  282: 	struct scheduled_send * next;
  283: 	fd_set writefds;
  284: 	struct timeval deadline;
  285: 	struct timeval now;
  286: 	struct timeval timeout;
  287: 	int max_fd;
  288: 
  289: 	if(upnp_gettimeofday(&deadline) < 0) {
  290: 		syslog(LOG_ERR, "gettimeofday: %m");
  291: 		return;
  292: 	}
  293: 	deadline.tv_usec += FINALIZE_SENDTO_DELAY*1000;
  294: 	if(deadline.tv_usec > 1000000) {
  295: 		deadline.tv_sec++;
  296: 		deadline.tv_usec -= 1000000;
  297: 	}
  298: 	while(send_list.lh_first) {
  299: 		FD_ZERO(&writefds);
  300: 		max_fd = -1;
  301: 		for(elt = send_list.lh_first; elt != NULL; elt = next) {
  302: 			next = elt->entries.le_next;
  303: 			syslog(LOG_DEBUG, "finalize_sendto(): %d bytes on socket %d",
  304: 			       (int)elt->len, elt->sockfd);
  305: 			n = send_from_to(elt->sockfd, elt->buf, elt->len, elt->flags,
  306: 			                 elt->src_addr, elt->dest_addr, elt->addrlen);
  307: 			/*n = sendto(elt->sockfd, elt->buf, elt->len, elt->flags,
  308: 			           elt->dest_addr, elt->addrlen);*/
  309: 			if(n < 0) {
  310: 				if(errno==EAGAIN || errno==EWOULDBLOCK) {
  311: 					FD_SET(elt->sockfd, &writefds);
  312: 					if(elt->sockfd > max_fd)
  313: 						max_fd = elt->sockfd;
  314: 					continue;
  315: 				}
  316: 				syslog(LOG_WARNING, "finalize_sendto(): socket=%d sendto: %m", elt->sockfd);
  317: 			}
  318: 			/* remove from the list */
  319: 			LIST_REMOVE(elt, entries);
  320: 			free(elt);
  321: 		}
  322: 		/* check deadline */
  323: 		if(upnp_gettimeofday(&now) < 0) {
  324: 			syslog(LOG_ERR, "gettimeofday: %m");
  325: 			return;
  326: 		}
  327: 		if(now.tv_sec > deadline.tv_sec ||
  328: 		   (now.tv_sec == deadline.tv_sec && now.tv_usec > deadline.tv_usec)) {
  329: 			/* deadline ! */
  330: 			while((elt = send_list.lh_first) != NULL) {
  331: 				LIST_REMOVE(elt, entries);
  332: 				free(elt);
  333: 			}
  334: 			return;
  335: 		}
  336: 		/* compute timeout value */
  337: 		timeout.tv_sec = deadline.tv_sec - now.tv_sec;
  338: 		timeout.tv_usec = deadline.tv_usec - now.tv_usec;
  339: 		if(timeout.tv_usec < 0) {
  340: 			timeout.tv_sec--;
  341: 			timeout.tv_usec += 1000000;
  342: 		}
  343: 		if(max_fd >= 0) {
  344: 			if(select(max_fd + 1, NULL, &writefds, NULL, &timeout) < 0) {
  345: 				syslog(LOG_ERR, "select: %m");
  346: 				return;
  347: 			}
  348: 		}
  349: 	}
  350: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>