Annotation of embedaddon/miniupnpd/minissdpd/asyncsendto.c, revision 1.1.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>