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>