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

1.1     ! misho       1: /* $Id: upnpredirect.c,v 1.49 2009/12/22 17:20:10 nanard Exp $ */
        !             2: /* MiniUPnP project
        !             3:  * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
        !             4:  * (c) 2006-2009 Thomas Bernard 
        !             5:  * This software is subject to the conditions detailed
        !             6:  * in the LICENCE file provided within the distribution */
        !             7: 
        !             8: #include <stdlib.h>
        !             9: #include <string.h>
        !            10: #include <syslog.h>
        !            11: #include <sys/types.h>
        !            12: #include <sys/socket.h>
        !            13: #include <netinet/in.h>
        !            14: #include <net/if.h>
        !            15: #include <arpa/inet.h>
        !            16: 
        !            17: #include <stdio.h>
        !            18: #include <ctype.h>
        !            19: #include <unistd.h>
        !            20: 
        !            21: #include "config.h"
        !            22: #include "upnpredirect.h"
        !            23: #include "upnpglobalvars.h"
        !            24: #include "upnpevents.h"
        !            25: #if defined(USE_NETFILTER)
        !            26: #include "netfilter/iptcrdr.h"
        !            27: #endif
        !            28: #if defined(USE_PF)
        !            29: #include "pf/obsdrdr.h"
        !            30: #endif
        !            31: #if defined(USE_IPF)
        !            32: #include "ipf/ipfrdr.h"
        !            33: #endif
        !            34: #if defined(USE_IPFW)
        !            35: #include "ipfw/ipfwrdr.h"
        !            36: #endif
        !            37: #ifdef USE_MINIUPNPDCTL
        !            38: #include <stdio.h>
        !            39: #include <unistd.h>
        !            40: #endif
        !            41: #ifdef ENABLE_LEASEFILE
        !            42: #include <sys/stat.h>
        !            43: #endif
        !            44: 
        !            45: /* from <inttypes.h> */
        !            46: #ifndef PRIu64
        !            47: #define PRIu64 "llu"
        !            48: #endif
        !            49: 
        !            50: /* proto_atoi() 
        !            51:  * convert the string "UDP" or "TCP" to IPPROTO_UDP and IPPROTO_UDP */
        !            52: static int
        !            53: proto_atoi(const char * protocol)
        !            54: {
        !            55:        int proto = IPPROTO_TCP;
        !            56:        if(strcmp(protocol, "UDP") == 0)
        !            57:                proto = IPPROTO_UDP;
        !            58:        return proto;
        !            59: }
        !            60: 
        !            61: #ifdef ENABLE_LEASEFILE
        !            62: static int lease_file_add( unsigned short eport, const char * iaddr, unsigned short iport, int proto, const char * desc)
        !            63: {
        !            64:        FILE * fd;
        !            65: 
        !            66:        if (lease_file == NULL) return 0;
        !            67: 
        !            68:        fd = fopen( lease_file, "a");
        !            69:        if (fd==NULL) {
        !            70:                syslog(LOG_ERR, "could not open lease file: %s", lease_file);
        !            71:                return -1;
        !            72:        }
        !            73: 
        !            74:        fprintf( fd, "%s:%hu:%s:%hu:%s\n",
        !            75:                 ((proto==IPPROTO_TCP)?"TCP":"UDP"), eport, iaddr, iport, desc);
        !            76:        fclose(fd);
        !            77:        
        !            78:        return 0;
        !            79: }
        !            80: 
        !            81: static int lease_file_remove( unsigned short eport, int proto)
        !            82: {
        !            83:        FILE* fd, *fdt;
        !            84:        int tmp;
        !            85:        char buf[512];
        !            86:        char str[32];
        !            87:        char tmpfilename[128];
        !            88:        int str_size, buf_size;
        !            89: 
        !            90: 
        !            91:        if (lease_file == NULL) return 0;
        !            92: 
        !            93:        if (strlen( lease_file) + 7 > sizeof(tmpfilename)) {
        !            94:                syslog(LOG_ERR, "Lease filename is too long");
        !            95:                return -1;
        !            96:        }
        !            97: 
        !            98:        strncpy( tmpfilename, lease_file, sizeof(tmpfilename) );
        !            99:        strncat( tmpfilename, "XXXXXX", sizeof(tmpfilename) - strlen(tmpfilename));
        !           100: 
        !           101:        fd = fopen( lease_file, "r");
        !           102:        if (fd==NULL) {
        !           103:                return 0;
        !           104:        }
        !           105: 
        !           106:        snprintf( str, sizeof(str), "%s:%u", ((proto==IPPROTO_TCP)?"TCP":"UDP"), eport);
        !           107:        str_size = strlen(str);
        !           108: 
        !           109:        tmp = mkstemp(tmpfilename);
        !           110:        if (tmp==-1) {
        !           111:                fclose(fd);
        !           112:                syslog(LOG_ERR, "could not open temporary lease file");
        !           113:                return -1;
        !           114:        }
        !           115:        fchmod(tmp, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
        !           116:        fdt = fdopen(tmp, "a");
        !           117: 
        !           118:        buf[sizeof(buf)-1] = 0;
        !           119:        while( fgets( buf, sizeof(buf)-1, fd) != NULL) {
        !           120:                buf_size = strlen(buf);
        !           121: 
        !           122:                if (buf_size < str_size || strncmp( str, buf, str_size)!=0) {
        !           123:                        fwrite(buf, buf_size, 1, fdt);
        !           124:                }
        !           125:        }
        !           126:        fclose(fdt);
        !           127:        fclose(fd);
        !           128:        
        !           129:        if (rename( tmpfilename, lease_file) < 0) {
        !           130:                syslog(LOG_ERR, "could not rename temporary lease file to %s", lease_file);
        !           131:                remove( tmpfilename);
        !           132:        }
        !           133:        
        !           134:        return 0;
        !           135:        
        !           136: }
        !           137: 
        !           138: /* reload_from_lease_file()
        !           139:  * read lease_file and add the rules contained
        !           140:  */
        !           141: int reload_from_lease_file()
        !           142: {
        !           143:        FILE * fd;
        !           144:        char * p;
        !           145:        unsigned short eport, iport;
        !           146:        char * proto;
        !           147:        char * iaddr;
        !           148:        char * desc;
        !           149:        char line[128];
        !           150:        int r;
        !           151: 
        !           152:        if(!lease_file) return -1;
        !           153:        fd = fopen( lease_file, "r");
        !           154:        if (fd==NULL) {
        !           155:                syslog(LOG_ERR, "could not open lease file: %s", lease_file);
        !           156:                return -1;
        !           157:        }
        !           158:        if(unlink(lease_file) < 0) {
        !           159:                syslog(LOG_WARNING, "could not unlink file %s : %m", lease_file);
        !           160:        }
        !           161: 
        !           162:        while(fgets(line, sizeof(line), fd)) {
        !           163:                syslog(LOG_DEBUG, "parsing lease file line '%s'", line);
        !           164:                proto = line;
        !           165:                p = strchr(line, ':');
        !           166:                if(!p) {
        !           167:                        syslog(LOG_ERR, "unrecognized data in lease file");
        !           168:                        continue;
        !           169:                }
        !           170:                *(p++) = '\0';
        !           171:                iaddr = strchr(p, ':');
        !           172:                if(!iaddr) {
        !           173:                        syslog(LOG_ERR, "unrecognized data in lease file");
        !           174:                        continue;
        !           175:                }
        !           176:                *(iaddr++) = '\0';
        !           177:                eport = (unsigned short)atoi(p);
        !           178:                p = strchr(iaddr, ':');
        !           179:                if(!p) {
        !           180:                        syslog(LOG_ERR, "unrecognized data in lease file");
        !           181:                        continue;
        !           182:                }
        !           183:                *(p++) = '\0';
        !           184:                desc = strchr(p, ':');
        !           185:                if(!desc) {
        !           186:                        syslog(LOG_ERR, "unrecognized data in lease file");
        !           187:                        continue;
        !           188:                }
        !           189:                *(desc++) = '\0';
        !           190:                iport = (unsigned short)atoi(p);
        !           191:                /* trim description */
        !           192:                while(isspace(*desc))
        !           193:                        desc++;
        !           194:                p = desc;
        !           195:                while(*(p+1))
        !           196:                        p++;
        !           197:                while(isspace(*p) && (p > desc))
        !           198:                        *(p--) = '\0';
        !           199: 
        !           200:                r = upnp_redirect(eport, iaddr, iport, proto, desc);
        !           201:                if(r == -1) {
        !           202:                        syslog(LOG_ERR, "Failed to redirect %hu -> %s:%hu protocol %s",
        !           203:                               eport, iaddr, iport, proto);
        !           204:                } else if(r == -2) {
        !           205:                        /* Add the redirection again to the lease file */
        !           206:                        lease_file_add(eport, iaddr, iport, proto_atoi(proto), desc);
        !           207:                }
        !           208:        }
        !           209:        fclose(fd);
        !           210:        
        !           211:        return 0;
        !           212: }
        !           213: #endif
        !           214: 
        !           215: /* upnp_redirect() 
        !           216:  * calls OS/fw dependant implementation of the redirection.
        !           217:  * protocol should be the string "TCP" or "UDP"
        !           218:  * returns: 0 on success
        !           219:  *          -1 failed to redirect
        !           220:  *          -2 already redirected
        !           221:  *          -3 permission check failed
        !           222:  */
        !           223: int
        !           224: upnp_redirect(unsigned short eport, 
        !           225:               const char * iaddr, unsigned short iport,
        !           226:               const char * protocol, const char * desc)
        !           227: {
        !           228:        int proto, r;
        !           229:        char iaddr_old[32];
        !           230:        unsigned short iport_old;
        !           231:        struct in_addr address;
        !           232:        proto = proto_atoi(protocol);
        !           233:        if(inet_aton(iaddr, &address) < 0) {
        !           234:                syslog(LOG_ERR, "inet_aton(%s) : %m", iaddr);
        !           235:                return -1;
        !           236:        }
        !           237: 
        !           238:        if(!check_upnp_rule_against_permissions(upnppermlist, num_upnpperm,
        !           239:                                                eport, address, iport)) {
        !           240:                syslog(LOG_INFO, "redirection permission check failed for "
        !           241:                                 "%hu->%s:%hu %s", eport, iaddr, iport, protocol);
        !           242:                return -3;
        !           243:        }
        !           244:        r = get_redirect_rule(ext_if_name, eport, proto,
        !           245:                              iaddr_old, sizeof(iaddr_old), &iport_old, 0, 0, 0, 0);
        !           246:        if(r == 0) {
        !           247:                /* if existing redirect rule matches redirect request return success
        !           248:                 * xbox 360 does not keep track of the port it redirects and will
        !           249:                 * redirect another port when receiving ConflictInMappingEntry */
        !           250:                if(strcmp(iaddr,iaddr_old)==0 && iport==iport_old) {
        !           251:                        syslog(LOG_INFO, "ignoring redirect request as it matches existing redirect");
        !           252:                } else {
        !           253: 
        !           254:                        syslog(LOG_INFO, "port %hu protocol %s already redirected to %s:%hu",
        !           255:                                eport, protocol, iaddr_old, iport_old);
        !           256:                        return -2;
        !           257:                }
        !           258:        } else {
        !           259:                syslog(LOG_INFO, "redirecting port %hu to %s:%hu protocol %s for: %s",
        !           260:                        eport, iaddr, iport, protocol, desc);                   
        !           261:                return upnp_redirect_internal(eport, iaddr, iport, proto, desc);
        !           262:        }
        !           263: 
        !           264:        return 0;
        !           265: }
        !           266: 
        !           267: int
        !           268: upnp_redirect_internal(unsigned short eport,
        !           269:                        const char * iaddr, unsigned short iport,
        !           270:                        int proto, const char * desc)
        !           271: {
        !           272:        /*syslog(LOG_INFO, "redirecting port %hu to %s:%hu protocol %s for: %s",
        !           273:                eport, iaddr, iport, protocol, desc);                   */
        !           274:        if(add_redirect_rule2(ext_if_name, eport, iaddr, iport, proto, desc) < 0) {
        !           275:                return -1;
        !           276:        }
        !           277: 
        !           278: #ifdef ENABLE_LEASEFILE
        !           279:        lease_file_add( eport, iaddr, iport, proto, desc);
        !           280: #endif
        !           281: /*     syslog(LOG_INFO, "creating pass rule to %s:%hu protocol %s for: %s",
        !           282:                iaddr, iport, protocol, desc);*/
        !           283:        if(add_filter_rule2(ext_if_name, iaddr, eport, iport, proto, desc) < 0) {
        !           284:                /* clean up the redirect rule */
        !           285: #if !defined(__linux__)
        !           286:                delete_redirect_rule(ext_if_name, eport, proto);
        !           287: #endif
        !           288:                return -1;
        !           289:        }
        !           290: #ifdef ENABLE_EVENTS
        !           291:        upnp_event_var_change_notify(EWanIPC);
        !           292: #endif
        !           293:        return 0;
        !           294: }
        !           295: 
        !           296: 
        !           297: 
        !           298: int
        !           299: upnp_get_redirection_infos(unsigned short eport, const char * protocol,
        !           300:                            unsigned short * iport,
        !           301:                            char * iaddr, int iaddrlen,
        !           302:                            char * desc, int desclen)
        !           303: {
        !           304:        if(desc && (desclen > 0))
        !           305:                desc[0] = '\0';
        !           306:        return get_redirect_rule(ext_if_name, eport, proto_atoi(protocol),
        !           307:                                 iaddr, iaddrlen, iport, desc, desclen, 0, 0);
        !           308: }
        !           309: 
        !           310: int
        !           311: upnp_get_redirection_infos_by_index(int index,
        !           312:                                     unsigned short * eport, char * protocol,
        !           313:                                     unsigned short * iport, 
        !           314:                                     char * iaddr, int iaddrlen,
        !           315:                                     char * desc, int desclen)
        !           316: {
        !           317:        /*char ifname[IFNAMSIZ];*/
        !           318:        int proto = 0;
        !           319: 
        !           320:        if(desc && (desclen > 0))
        !           321:                desc[0] = '\0';
        !           322:        if(get_redirect_rule_by_index(index, 0/*ifname*/, eport, iaddr, iaddrlen,
        !           323:                                      iport, &proto, desc, desclen, 0, 0) < 0)
        !           324:                return -1;
        !           325:        else
        !           326:        {
        !           327:                if(proto == IPPROTO_TCP)
        !           328:                        memcpy(protocol, "TCP", 4);
        !           329:                else
        !           330:                        memcpy(protocol, "UDP", 4);
        !           331:                return 0;
        !           332:        }
        !           333: }
        !           334: 
        !           335: int
        !           336: _upnp_delete_redir(unsigned short eport, int proto)
        !           337: {
        !           338:        int r;
        !           339: #if defined(__linux__)
        !           340:        r = delete_redirect_and_filter_rules(eport, proto);
        !           341: #else
        !           342:        r = delete_redirect_rule(ext_if_name, eport, proto);
        !           343:        delete_filter_rule(ext_if_name, eport, proto);
        !           344: #endif
        !           345: #ifdef ENABLE_LEASEFILE
        !           346:        lease_file_remove( eport, proto);
        !           347: #endif
        !           348: 
        !           349: #ifdef ENABLE_EVENTS
        !           350:        upnp_event_var_change_notify(EWanIPC);
        !           351: #endif
        !           352:        return r;
        !           353: }
        !           354: 
        !           355: int
        !           356: upnp_delete_redirection(unsigned short eport, const char * protocol)
        !           357: {
        !           358:        syslog(LOG_INFO, "removing redirect rule port %hu %s", eport, protocol);
        !           359:        return _upnp_delete_redir(eport, proto_atoi(protocol));
        !           360: }
        !           361: 
        !           362: /* upnp_get_portmapping_number_of_entries()
        !           363:  * TODO: improve this code */
        !           364: int
        !           365: upnp_get_portmapping_number_of_entries()
        !           366: {
        !           367:        int n = 0, r = 0;
        !           368:        unsigned short eport, iport;
        !           369:        char protocol[4], iaddr[32], desc[64];
        !           370:        do {
        !           371:                protocol[0] = '\0'; iaddr[0] = '\0'; desc[0] = '\0';
        !           372:                r = upnp_get_redirection_infos_by_index(n, &eport, protocol, &iport,
        !           373:                                                        iaddr, sizeof(iaddr),
        !           374:                                                        desc, sizeof(desc) );
        !           375:                n++;
        !           376:        } while(r==0);
        !           377:        return (n-1);
        !           378: }
        !           379: 
        !           380: /* functions used to remove unused rules */
        !           381: struct rule_state *
        !           382: get_upnp_rules_state_list(int max_rules_number_target)
        !           383: {
        !           384:        /*char ifname[IFNAMSIZ];*/
        !           385:        int proto;
        !           386:        unsigned short iport;
        !           387:        struct rule_state * tmp;
        !           388:        struct rule_state * list = 0;
        !           389:        int i = 0;
        !           390:        /*ifname[0] = '\0';*/
        !           391:        tmp = malloc(sizeof(struct rule_state));
        !           392:        if(!tmp)
        !           393:                return 0;
        !           394:        while(get_redirect_rule_by_index(i, /*ifname*/0, &tmp->eport, 0, 0,
        !           395:                                      &iport, &proto, 0, 0,
        !           396:                                                                  &tmp->packets, &tmp->bytes) >= 0)
        !           397:        {
        !           398:                tmp->proto = (short)proto;
        !           399:                /* add tmp to list */
        !           400:                tmp->next = list;
        !           401:                list = tmp;
        !           402:                /* prepare next iteration */
        !           403:                i++;
        !           404:                tmp = malloc(sizeof(struct rule_state));
        !           405:                if(!tmp)
        !           406:                        break;
        !           407:        }
        !           408:        free(tmp);
        !           409:        /* return empty list if not enough redirections */
        !           410:        if(i<=max_rules_number_target)
        !           411:                while(list)
        !           412:                {
        !           413:                        tmp = list;
        !           414:                        list = tmp->next;
        !           415:                        free(tmp);
        !           416:                }
        !           417:        /* return list */
        !           418:        return list;
        !           419: }
        !           420: 
        !           421: void
        !           422: remove_unused_rules(struct rule_state * list)
        !           423: {
        !           424:        char ifname[IFNAMSIZ];
        !           425:        unsigned short iport;
        !           426:        struct rule_state * tmp;
        !           427:        u_int64_t packets;
        !           428:        u_int64_t bytes;
        !           429:        int n = 0;
        !           430:        while(list)
        !           431:        {
        !           432:                /* remove the rule if no traffic has used it */
        !           433:                if(get_redirect_rule(ifname, list->eport, list->proto,
        !           434:                                 0, 0, &iport, 0, 0, &packets, &bytes) >= 0)
        !           435:                {
        !           436:                        if(packets == list->packets && bytes == list->bytes)
        !           437:                        {
        !           438:                                _upnp_delete_redir(list->eport, list->proto);
        !           439:                                n++;
        !           440:                        }
        !           441:                }
        !           442:                tmp = list;
        !           443:                list = tmp->next;
        !           444:                free(tmp);
        !           445:        }
        !           446:        if(n>0)
        !           447:                syslog(LOG_NOTICE, "removed %d unused rules", n);
        !           448: }
        !           449: 
        !           450: 
        !           451: /* stuff for miniupnpdctl */
        !           452: #ifdef USE_MINIUPNPDCTL
        !           453: void
        !           454: write_ruleset_details(int s)
        !           455: {
        !           456:        char ifname[IFNAMSIZ];
        !           457:        int proto = 0;
        !           458:        unsigned short eport, iport;
        !           459:        char desc[64];
        !           460:        char iaddr[32];
        !           461:        u_int64_t packets;
        !           462:        u_int64_t bytes;
        !           463:        int i = 0;
        !           464:        char buffer[256];
        !           465:        int n;
        !           466:        ifname[0] = '\0';
        !           467:        write(s, "Ruleset :\n", 10);
        !           468:        while(get_redirect_rule_by_index(i, ifname, &eport, iaddr, sizeof(iaddr),
        !           469:                                         &iport, &proto, desc, sizeof(desc),
        !           470:                                         &packets, &bytes) >= 0)
        !           471:        {
        !           472:                n = snprintf(buffer, sizeof(buffer), "%2d %s %s %hu->%s:%hu "
        !           473:                                                     "'%s' %" PRIu64 " %" PRIu64 "\n",
        !           474:                                                     /*"'%s' %llu %llu\n",*/
        !           475:                             i, ifname, proto==IPPROTO_TCP?"TCP":"UDP",
        !           476:                             eport, iaddr, iport, desc, packets, bytes);
        !           477:                write(s, buffer, n);
        !           478:                i++;
        !           479:        }
        !           480: }
        !           481: #endif
        !           482: 

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