Annotation of embedaddon/miniupnpd/natpmp.c, revision 1.1

1.1     ! misho       1: /* $Id: natpmp.c,v 1.20 2010/05/06 13:42:47 nanard Exp $ */
        !             2: /* MiniUPnP project
        !             3:  * (c) 2007-2010 Thomas Bernard
        !             4:  * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
        !             5:  * This software is subject to the conditions detailed
        !             6:  * in the LICENCE file provided within the distribution */
        !             7: #include <stdio.h>
        !             8: #include <string.h>
        !             9: #include <unistd.h>
        !            10: #include <syslog.h>
        !            11: #include <time.h>
        !            12: #include <sys/types.h>
        !            13: #include <sys/socket.h>
        !            14: #include <netinet/in.h>
        !            15: #include <arpa/inet.h>
        !            16: #include "config.h"
        !            17: #include "natpmp.h"
        !            18: #include "upnpglobalvars.h"
        !            19: #include "getifaddr.h"
        !            20: #include "upnpredirect.h"
        !            21: #include "commonrdr.h"
        !            22: 
        !            23: #ifdef ENABLE_NATPMP
        !            24: 
        !            25: int OpenAndConfNATPMPSocket(in_addr_t addr)
        !            26: {
        !            27:        int snatpmp;
        !            28:        snatpmp = socket(PF_INET, SOCK_DGRAM, 0/*IPPROTO_UDP*/);
        !            29:        if(snatpmp<0)
        !            30:        {
        !            31:                syslog(LOG_ERR, "socket(natpmp): %m");
        !            32:                return -1;
        !            33:        }
        !            34:        else
        !            35:        {
        !            36:                struct sockaddr_in natpmp_addr;
        !            37:                memset(&natpmp_addr, 0, sizeof(natpmp_addr));
        !            38:                natpmp_addr.sin_family = AF_INET;
        !            39:                natpmp_addr.sin_port = htons(NATPMP_PORT);
        !            40:                //natpmp_addr.sin_addr.s_addr = INADDR_ANY;
        !            41:                natpmp_addr.sin_addr.s_addr = addr;
        !            42:                //natpmp_addr.sin_addr.s_addr = inet_addr("192.168.0.1");
        !            43:                if(bind(snatpmp, (struct sockaddr *)&natpmp_addr, sizeof(natpmp_addr)) < 0)
        !            44:                {
        !            45:                        syslog(LOG_ERR, "bind(natpmp): %m");
        !            46:                        close(snatpmp);
        !            47:                        return -1;
        !            48:                }
        !            49:        }
        !            50:        return snatpmp;
        !            51: }
        !            52: 
        !            53: int OpenAndConfNATPMPSockets(int * sockets)
        !            54: {
        !            55:        int i, j;
        !            56:        for(i=0; i<n_lan_addr; i++)
        !            57:        {
        !            58:                sockets[i] = OpenAndConfNATPMPSocket(lan_addr[i].addr.s_addr);
        !            59:                if(sockets[i] < 0)
        !            60:                {
        !            61:                        for(j=0; j<i; j++)
        !            62:                        {
        !            63:                                close(sockets[j]);
        !            64:                                sockets[j] = -1;
        !            65:                        }
        !            66:                        return -1;
        !            67:                }
        !            68:        }
        !            69:        return 0;
        !            70: }
        !            71: 
        !            72: static void FillPublicAddressResponse(unsigned char * resp, in_addr_t senderaddr)
        !            73: {
        !            74: #ifndef MULTIPLE_EXTERNAL_IP
        !            75:        char tmp[16];
        !            76: 
        !            77:        if(use_ext_ip_addr) {
        !            78:         inet_pton(AF_INET, use_ext_ip_addr, resp+8);
        !            79:        } else {
        !            80:                if(!ext_if_name || ext_if_name[0]=='\0') {
        !            81:                        resp[3] = 3;    /* Network Failure (e.g. NAT box itself
        !            82:                                         * has not obtained a DHCP lease) */
        !            83:                } else if(getifaddr(ext_if_name, tmp, INET_ADDRSTRLEN) < 0) {
        !            84:                        syslog(LOG_ERR, "Failed to get IP for interface %s", ext_if_name);
        !            85:                        resp[3] = 3;    /* Network Failure (e.g. NAT box itself
        !            86:                                         * has not obtained a DHCP lease) */
        !            87:                } else {
        !            88:                        inet_pton(AF_INET, tmp, resp+8);
        !            89:                }
        !            90:        }
        !            91: #else
        !            92:        int i;
        !            93: 
        !            94:        for(i = 0; i<n_lan_addr; i++) {
        !            95:                if( (senderaddr & lan_addr[i].mask.s_addr)
        !            96:                   == (lan_addr[i].addr.s_addr & lan_addr[i].mask.s_addr)) {
        !            97:                        memcpy(resp+8, &lan_addr[i].ext_ip_addr,
        !            98:                               sizeof(lan_addr[i].ext_ip_addr));
        !            99:                        break;
        !           100:                }
        !           101:        }
        !           102: #endif
        !           103: }
        !           104: 
        !           105: /** read the request from the socket, process it and then send the
        !           106:  * response back.
        !           107:  */
        !           108: void ProcessIncomingNATPMPPacket(int s)
        !           109: {
        !           110:        unsigned char req[32];  /* request udp packet */
        !           111:        unsigned char resp[32]; /* response udp packet */
        !           112:        int resplen;
        !           113:        struct sockaddr_in senderaddr;
        !           114:        socklen_t senderaddrlen = sizeof(senderaddr);
        !           115:        int n;
        !           116:        char senderaddrstr[16];
        !           117:        n = recvfrom(s, req, sizeof(req), 0,
        !           118:                     (struct sockaddr *)&senderaddr, &senderaddrlen);
        !           119:        if(n<0) {
        !           120:                syslog(LOG_ERR, "recvfrom(natpmp): %m");
        !           121:                return;
        !           122:        }
        !           123:        if(!inet_ntop(AF_INET, &senderaddr.sin_addr,
        !           124:                      senderaddrstr, sizeof(senderaddrstr))) {
        !           125:                syslog(LOG_ERR, "inet_ntop(natpmp): %m");
        !           126:        }
        !           127:        syslog(LOG_INFO, "NAT-PMP request received from %s:%hu %dbytes",
        !           128:            senderaddrstr, ntohs(senderaddr.sin_port), n);
        !           129:        if(n<2 || ((((req[1]-1)&~1)==0) && n<12)) {
        !           130:                syslog(LOG_WARNING, "discarding NAT-PMP request (too short) %dBytes",
        !           131:                       n);
        !           132:                return;
        !           133:        }
        !           134:        if(req[1] & 128) {
        !           135:                /* discarding NAT-PMP responses silently */
        !           136:                return;
        !           137:        }
        !           138:        memset(resp, 0, sizeof(resp));
        !           139:        resplen = 8;
        !           140:        resp[1] = 128 + req[1]; /* response OPCODE is request OPCODE + 128 */
        !           141:        /* setting response TIME STAMP */
        !           142:        *((uint32_t *)(resp+4)) = htonl(time(NULL) - startup_time);
        !           143:        if(req[0] > 0) {
        !           144:                /* invalid version */
        !           145:                syslog(LOG_WARNING, "unsupported NAT-PMP version : %u",
        !           146:                       (unsigned)req[0]);
        !           147:                resp[3] = 1;    /* unsupported version */
        !           148:        } else switch(req[1]) {
        !           149:        case 0: /* Public address request */
        !           150:                syslog(LOG_INFO, "NAT-PMP public address request");
        !           151:                FillPublicAddressResponse(resp, senderaddr.sin_addr.s_addr);
        !           152:                resplen = 12;
        !           153:                break;
        !           154:        case 1: /* UDP port mapping request */
        !           155:        case 2: /* TCP port mapping request */
        !           156:                {
        !           157:                        unsigned short iport;   /* private port */
        !           158:                        unsigned short eport;   /* public port */
        !           159:                        uint32_t lifetime;              /* lifetime=0 => remove port mapping */
        !           160:                        int r;
        !           161:                        int proto;
        !           162:                        char iaddr_old[16];
        !           163:                        unsigned short iport_old;
        !           164:                        iport = ntohs(*((uint16_t *)(req+4)));
        !           165:                        eport = ntohs(*((uint16_t *)(req+6)));
        !           166:                        lifetime = ntohl(*((uint32_t *)(req+8)));
        !           167:                        proto = (req[1]==1)?IPPROTO_UDP:IPPROTO_TCP;
        !           168:                        syslog(LOG_INFO, "NAT-PMP port mapping request : "
        !           169:                                         "%hu->%s:%hu %s lifetime=%us",
        !           170:                                         eport, senderaddrstr, iport,
        !           171:                                         (req[1]==1)?"udp":"tcp", lifetime);
        !           172:                        if(eport==0)
        !           173:                                eport = iport;
        !           174:                        /* TODO: accept port mapping if iport ok but eport not ok
        !           175:                         * (and set eport correctly) */
        !           176:                        if(lifetime == 0) {
        !           177:                                /* remove the mapping */
        !           178:                                if(iport == 0) {
        !           179:                                        /* remove all the mappings for this client */
        !           180:                                        int index = 0;
        !           181:                                        unsigned short eport2, iport2;
        !           182:                                        char iaddr2[16];
        !           183:                                        int proto2;
        !           184:                                        char desc[64];
        !           185:                                        while(get_redirect_rule_by_index(index, 0,
        !           186:                                                  &eport2, iaddr2, sizeof(iaddr2),
        !           187:                                                          &iport2, &proto2,
        !           188:                                                          desc, sizeof(desc), 0, 0) >= 0) {
        !           189:                                                syslog(LOG_DEBUG, "%d %d %hu->'%s':%hu '%s'",
        !           190:                                                       index, proto2, eport2, iaddr2, iport2, desc);
        !           191:                                                if(0 == strcmp(iaddr2, senderaddrstr)
        !           192:                                                  && 0 == memcmp(desc, "NAT-PMP ", 8)) {
        !           193:                                                        r = _upnp_delete_redir(eport2, proto2);
        !           194:                                                        /* TODO : check return value */
        !           195:                                                        if(r<0) {
        !           196:                                                                syslog(LOG_ERR, "failed to remove port mapping");
        !           197:                                                                index++;
        !           198:                                                        } else {
        !           199:                                                                syslog(LOG_INFO, "NAT-PMP %s port %hu mapping removed", proto2==IPPROTO_TCP?"TCP":"UDP", eport2);
        !           200:                                                        }
        !           201:                                                } else {
        !           202:                                                        index++;
        !           203:                                                }
        !           204:                                        }
        !           205:                                } else {
        !           206:                                        /* To improve the interworking between nat-pmp and
        !           207:                                         * UPnP, we should check that we remove only NAT-PMP 
        !           208:                                         * mappings */
        !           209:                                        r = _upnp_delete_redir(eport, proto);
        !           210:                                        /*syslog(LOG_DEBUG, "%hu %d r=%d", eport, proto, r);*/
        !           211:                                        if(r<0) {
        !           212:                                                syslog(LOG_ERR, "Failed to remove NAT-PMP mapping eport %hu, protocol %s", eport, (proto==IPPROTO_TCP)?"TCP":"UDP");
        !           213:                                                resp[3] = 2;    /* Not Authorized/Refused */
        !           214:                                        }
        !           215:                                }
        !           216:                                eport = 0; /* to indicate correct removing of port mapping */
        !           217:                        } else if(iport==0
        !           218:                           || !check_upnp_rule_against_permissions(upnppermlist, num_upnpperm, eport, senderaddr.sin_addr, iport)) {
        !           219:                                resp[3] = 2;    /* Not Authorized/Refused */
        !           220:                        } else do {
        !           221:                                r = get_redirect_rule(ext_if_name, eport, proto,
        !           222:                                                      iaddr_old, sizeof(iaddr_old),
        !           223:                                                      &iport_old, 0, 0, 0, 0);
        !           224:                                if(r==0) {
        !           225:                                        if(strcmp(senderaddrstr, iaddr_old)==0
        !           226:                                       && iport==iport_old) {
        !           227:                                                /* redirection allready existing */
        !           228:                                                syslog(LOG_INFO, "port %hu %s already redirected to %s:%hu, replacing", eport, (proto==IPPROTO_TCP)?"tcp":"udp", iaddr_old, iport_old);
        !           229:                                                /* remove and then add again */
        !           230:                                                if(_upnp_delete_redir(eport, proto) < 0) {
        !           231:                                                        syslog(LOG_ERR, "failed to remove port mapping");
        !           232:                                                        break;
        !           233:                                                }
        !           234:                                        } else {
        !           235:                                                eport++;
        !           236:                                                continue;
        !           237:                                        }
        !           238:                                }
        !           239:                                { /* do the redirection */
        !           240:                                        char desc[64];
        !           241:                                        unsigned timestamp = (unsigned)(time(NULL) - startup_time)
        !           242:                                                              + lifetime;
        !           243:                                        snprintf(desc, sizeof(desc), "NAT-PMP %u", timestamp);
        !           244:                                        /* TODO : check return code */
        !           245:                                        if(upnp_redirect_internal(eport, senderaddrstr,
        !           246:                                                                  iport, proto, desc) < 0) {
        !           247:                                                syslog(LOG_ERR, "Failed to add NAT-PMP %hu %s->%s:%hu '%s'",
        !           248:                                                       eport, (proto==IPPROTO_TCP)?"tcp":"udp", senderaddrstr, iport, desc);
        !           249:                                                resp[3] = 3;  /* Failure */
        !           250:                                        } else if( !nextnatpmptoclean_eport
        !           251:                                                 || timestamp < nextnatpmptoclean_timestamp) {
        !           252:                                                nextnatpmptoclean_timestamp = timestamp;
        !           253:                                                nextnatpmptoclean_eport = eport;
        !           254:                                                nextnatpmptoclean_proto = proto;
        !           255:                                        }
        !           256:                                        break;
        !           257:                                }
        !           258:                        } while(r==0);
        !           259:                        *((uint16_t *)(resp+8)) = htons(iport); /* private port */
        !           260:                        *((uint16_t *)(resp+10)) = htons(eport);        /* public port */
        !           261:                        *((uint32_t *)(resp+12)) = htonl(lifetime);
        !           262:                }
        !           263:                resplen = 16;
        !           264:                break;
        !           265:        default:
        !           266:                resp[3] = 5;    /* Unsupported OPCODE */
        !           267:        }
        !           268:        n = sendto(s, resp, resplen, 0,
        !           269:                   (struct sockaddr *)&senderaddr, sizeof(senderaddr));
        !           270:        if(n<0) {
        !           271:                syslog(LOG_ERR, "sendto(natpmp): %m");
        !           272:        } else if(n<resplen) {
        !           273:                syslog(LOG_ERR, "sendto(natpmp): sent only %d bytes out of %d",
        !           274:                       n, resplen);
        !           275:        }
        !           276: }
        !           277: 
        !           278: /* iterate through the redirection list to find those who came
        !           279:  * from NAT-PMP and select the first to expire */
        !           280: int ScanNATPMPforExpiration()
        !           281: {
        !           282:        char desc[64];
        !           283:        unsigned short iport, eport;
        !           284:        int proto;
        !           285:        int r, i;
        !           286:        unsigned timestamp;
        !           287:        nextnatpmptoclean_eport = 0;
        !           288:        nextnatpmptoclean_timestamp = 0;
        !           289:        for(i = 0; ; i++) {
        !           290:                r = get_redirect_rule_by_index(i, 0, &eport, 0, 0,
        !           291:                                               &iport, &proto, desc, sizeof(desc),
        !           292:                                               0, 0);
        !           293:                if(r<0)
        !           294:                        break;
        !           295:                if(sscanf(desc, "NAT-PMP %u", &timestamp) == 1) {
        !           296:                        if( !nextnatpmptoclean_eport
        !           297:                          || timestamp < nextnatpmptoclean_timestamp) {
        !           298:                                nextnatpmptoclean_eport = eport;
        !           299:                                nextnatpmptoclean_proto = proto;
        !           300:                                nextnatpmptoclean_timestamp = timestamp;
        !           301:                                syslog(LOG_DEBUG, "set nextnatpmptoclean_timestamp to %u", timestamp);
        !           302:                        }
        !           303:                }
        !           304:        }
        !           305:        return 0;
        !           306: }
        !           307: 
        !           308: /* remove the next redirection that is expired
        !           309:  */
        !           310: int CleanExpiredNATPMP()
        !           311: {
        !           312:        char desc[64];
        !           313:        unsigned timestamp;
        !           314:        unsigned short iport;
        !           315:        if(get_redirect_rule(ext_if_name, nextnatpmptoclean_eport,
        !           316:                             nextnatpmptoclean_proto,
        !           317:                             0, 0,
        !           318:                             &iport, desc, sizeof(desc), 0, 0) < 0)
        !           319:                return ScanNATPMPforExpiration();
        !           320:        /* check desc - this is important since we keep expiration time as part
        !           321:         * of the desc.
        !           322:         * If the rule is renewed, timestamp and nextnatpmptoclean_timestamp 
        !           323:         * can be different. In that case, the rule must not be removed ! */
        !           324:        if(sscanf(desc, "NAT-PMP %u", &timestamp) == 1) {
        !           325:                if(timestamp > nextnatpmptoclean_timestamp)
        !           326:                        return ScanNATPMPforExpiration();
        !           327:        }
        !           328:        /* remove redirection then search for next one:) */
        !           329:        if(_upnp_delete_redir(nextnatpmptoclean_eport, nextnatpmptoclean_proto)<0)
        !           330:                return -1;
        !           331:        syslog(LOG_INFO, "Expired NAT-PMP mapping port %hu %s removed",
        !           332:               nextnatpmptoclean_eport,
        !           333:               nextnatpmptoclean_proto==IPPROTO_TCP?"TCP":"UDP");
        !           334:        return ScanNATPMPforExpiration();
        !           335: }
        !           336: 
        !           337: /* SendNATPMPPublicAddressChangeNotification()
        !           338:  * should be called when the public IP address changed */
        !           339: void SendNATPMPPublicAddressChangeNotification(int * sockets, int n_sockets)
        !           340: {
        !           341:        struct sockaddr_in sockname;
        !           342:        unsigned char notif[12];
        !           343:        int j, n;
        !           344: 
        !           345:        notif[0] = 0;
        !           346:        notif[1] = 128;
        !           347:        notif[2] = 0;
        !           348:        notif[3] = 0;
        !           349:        *((uint32_t *)(notif+4)) = htonl(time(NULL) - startup_time);
        !           350: #ifndef MULTIPLE_EXTERNAL_IP
        !           351:        FillPublicAddressResponse(notif, 0);
        !           352:        if(notif[3])
        !           353:        {
        !           354:                syslog(LOG_WARNING, "%s: cannot get public IP address, stopping",
        !           355:                       "SendNATPMPPublicAddressChangeNotification");
        !           356:                return;
        !           357:        }
        !           358: #endif
        !           359:        memset(&sockname, 0, sizeof(struct sockaddr_in));
        !           360:     sockname.sin_family = AF_INET;
        !           361:     sockname.sin_port = htons(NATPMP_PORT);
        !           362:     sockname.sin_addr.s_addr = inet_addr(NATPMP_NOTIF_ADDR);
        !           363: 
        !           364:        for(j=0; j<n_sockets; j++)
        !           365:        {
        !           366:                if(sockets[j] < 0)
        !           367:                        continue;
        !           368: #ifdef MULTIPLE_EXTERNAL_IP
        !           369:                FillPublicAddressResponse(notif, lan_addr[j].addr.s_addr);
        !           370: #endif
        !           371:                n = sendto(sockets[j], notif, 12, 0,
        !           372:                           (struct sockaddr *)&sockname, sizeof(struct sockaddr_in));
        !           373:                if(n < 0)
        !           374:                {       
        !           375:                        syslog(LOG_ERR, "%s: sendto(s_udp=%d): %m",
        !           376:                               "SendNATPMPPublicAddressChangeNotification", sockets[j]);
        !           377:                        return;
        !           378:                }
        !           379:        }
        !           380: }
        !           381: 
        !           382: #endif
        !           383: 

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