Annotation of embedaddon/miniupnpd/minissdpd/asyncsendto.c, revision 1.1

1.1     ! misho       1: /* $Id: asyncsendto.c,v 1.12 2020/11/11 12:13:26 nanard 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>