|
|
| version 1.1.1.1, 2012/02/21 23:16:02 | version 1.1.1.2, 2012/05/29 12:55:57 |
|---|---|
| Line 1 | Line 1 |
| /* $Id$ */ | |
| /* | /* |
| * MiniUPnP project | * MiniUPnP project |
| * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ | * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ |
| * (c) 2009 Jardel Weyrich | * (c) 2009 Jardel Weyrich |
| * (c) 2011 Thomas Bernard | |
| * This software is subject to the conditions detailed | * This software is subject to the conditions detailed |
| * in the LICENCE file provided within the distribution | * in the LICENCE file provided within the distribution |
| */ | */ |
| #include "../config.h" | |
| #include <sys/param.h> | #include <sys/param.h> |
| #include <sys/types.h> | #include <sys/types.h> |
| #include <sys/file.h> | #include <sys/file.h> |
| Line 61 struct file; | Line 65 struct file; |
| #include <unistd.h> | #include <unistd.h> |
| #include <netinet/ip_fw.h> | #include <netinet/ip_fw.h> |
| #include "ipfwaux.h" | #include "ipfwaux.h" |
| #include "ipfwrdr.h" | |
| #include "../config.h" | |
| #include "../upnpglobalvars.h" | #include "../upnpglobalvars.h" |
| /* init and shutdown functions */ | |
| int init_redirect(void) { | int init_redirect(void) { |
| ipfw_exec(IP_FW_INIT, NULL, 0); | return ipfw_exec(IP_FW_INIT, NULL, 0); |
| return 0; | |
| } | } |
| void shutdown_redirect(void) { | void shutdown_redirect(void) { |
| ipfw_exec(IP_FW_TERM, NULL, 0); | ipfw_exec(IP_FW_TERM, NULL, 0); |
| } | } |
| /* ipfw cannot store descriptions and timestamp for port mappings so we keep | |
| * our own list in memory */ | |
| struct mapping_desc_time { | |
| struct mapping_desc_time * next; | |
| unsigned int timestamp; | |
| unsigned short eport; | |
| short proto; | |
| char desc[]; | |
| }; | |
| static struct mapping_desc_time * mappings_list = NULL; | |
| /* add an element to the port mappings descriptions & timestamp list */ | |
| static void | |
| add_desc_time(unsigned short eport, int proto, | |
| const char * desc, unsigned int timestamp) | |
| { | |
| struct mapping_desc_time * tmp; | |
| size_t l; | |
| if(!desc) | |
| desc = "miniupnpd"; | |
| l = strlen(desc) + 1; | |
| tmp = malloc(sizeof(struct mapping_desc_time) + l); | |
| if(tmp) { | |
| /* fill the element and insert it as head of the list */ | |
| tmp->next = mappings_list; | |
| tmp->timestamp = timestamp; | |
| tmp->eport = eport; | |
| tmp->proto = (short)proto; | |
| memcpy(tmp->desc, desc, l); | |
| mappings_list = tmp; | |
| } | |
| } | |
| /* remove an element to the port mappings descriptions & timestamp list */ | |
| static void | |
| del_desc_time(unsigned short eport, int proto) | |
| { | |
| struct mapping_desc_time * e; | |
| struct mapping_desc_time * * p; | |
| p = &mappings_list; | |
| e = *p; | |
| while(e) { | |
| if(e->eport == eport && e->proto == (short)proto) { | |
| *p = e->next; | |
| free(e); | |
| return; | |
| } else { | |
| p = &e->next; | |
| e = *p; | |
| } | |
| } | |
| } | |
| /* go through the list and find the description and timestamp */ | |
| static void | |
| get_desc_time(unsigned short eport, int proto, | |
| char * desc, int desclen, | |
| unsigned int * timestamp) | |
| { | |
| struct mapping_desc_time * e; | |
| for(e = mappings_list; e; e = e->next) { | |
| if(e->eport == eport && e->proto == (short)proto) { | |
| if(desc) | |
| strlcpy(desc, e->desc, desclen); | |
| if(timestamp) | |
| *timestamp = e->timestamp; | |
| return; | |
| } | |
| } | |
| } | |
| /* --- */ | |
| int add_redirect_rule2( | int add_redirect_rule2( |
| const char * ifname, | const char * ifname, |
| const char * rhost, | |
| unsigned short eport, | unsigned short eport, |
| const char * iaddr, | const char * iaddr, |
| unsigned short iport, | unsigned short iport, |
| int proto, | int proto, |
| const char * desc) | const char * desc, |
| unsigned int timestamp) | |
| { | { |
| struct ip_fw rule; | struct ip_fw rule; |
| int r; | |
| if (ipfw_validate_protocol(proto) < 0) | if (ipfw_validate_protocol(proto) < 0) |
| return -1; | return -1; |
| Line 93 int add_redirect_rule2( | Line 174 int add_redirect_rule2( |
| memset(&rule, 0, sizeof(struct ip_fw)); | memset(&rule, 0, sizeof(struct ip_fw)); |
| rule.version = IP_FW_CURRENT_API_VERSION; | rule.version = IP_FW_CURRENT_API_VERSION; |
| //rule.fw_number = 1000; // rule number | //rule.fw_number = 1000; // rule number |
| rule.context = (void *)desc; // TODO keep this? | //rule.context = (void *)desc; // The description is kept in a separate list |
| rule.fw_prot = proto; // protocol | rule.fw_prot = proto; // protocol |
| rule.fw_flg |= IP_FW_F_IIFACE; // interfaces to check | rule.fw_flg |= IP_FW_F_IIFACE; // interfaces to check |
| rule.fw_flg |= IP_FW_F_IIFNAME; // interfaces to check by name | rule.fw_flg |= IP_FW_F_IIFNAME; // interfaces to check by name |
| Line 101 int add_redirect_rule2( | Line 182 int add_redirect_rule2( |
| rule.fw_flg |= IP_FW_F_FWD; // forward action | rule.fw_flg |= IP_FW_F_FWD; // forward action |
| #ifdef USE_IFNAME_IN_RULES | #ifdef USE_IFNAME_IN_RULES |
| if (ifname != NULL) { | if (ifname != NULL) { |
| strcpy(rule.fw_in_if.fu_via_if.name, ifname); // src interface | strlcpy(rule.fw_in_if.fu_via_if.name, ifname, IFNAMSIZ); // src interface |
| rule.fw_in_if.fu_via_if.unit = -1; | rule.fw_in_if.fu_via_if.unit = -1; |
| } | } |
| #endif | #endif |
| Line 111 int add_redirect_rule2( | Line 192 int add_redirect_rule2( |
| } | } |
| memcpy(&rule.fw_dst, &rule.fw_out_if.fu_via_ip, sizeof(struct in_addr)); | memcpy(&rule.fw_dst, &rule.fw_out_if.fu_via_ip, sizeof(struct in_addr)); |
| memcpy(&rule.fw_fwd_ip.sin_addr, &rule.fw_out_if.fu_via_ip, sizeof(struct in_addr)); | memcpy(&rule.fw_fwd_ip.sin_addr, &rule.fw_out_if.fu_via_ip, sizeof(struct in_addr)); |
| rule.fw_dmsk.s_addr = INADDR_BROADCAST; | rule.fw_dmsk.s_addr = INADDR_BROADCAST; //TODO check this |
| IP_FW_SETNDSTP(&rule, 1); // number of external ports | IP_FW_SETNDSTP(&rule, 1); // number of external ports |
| rule.fw_uar.fw_pts[0] = eport; // external port | rule.fw_uar.fw_pts[0] = eport; // external port |
| rule.fw_fwd_ip.sin_port = iport; // internal port | rule.fw_fwd_ip.sin_port = iport; // internal port |
| if (rhost && rhost[0] != '\0') { | |
| inet_aton(rhost, &rule.fw_src); | |
| rule.fw_smsk.s_addr = htonl(INADDR_NONE); | |
| } | |
| return ipfw_exec(IP_FW_ADD, &rule, sizeof(rule)); | r = ipfw_exec(IP_FW_ADD, &rule, sizeof(rule)); |
| if(r >= 0) | |
| add_desc_time(eport, proto, desc, timestamp); | |
| return r; | |
| } | } |
| /* get_redirect_rule() | /* get_redirect_rule() |
| Line 131 int get_redirect_rule( | Line 219 int get_redirect_rule( |
| unsigned short * iport, | unsigned short * iport, |
| char * desc, | char * desc, |
| int desclen, | int desclen, |
| char * rhost, | |
| int rhostlen, | |
| unsigned int * timestamp, | |
| u_int64_t * packets, | u_int64_t * packets, |
| u_int64_t * bytes) | u_int64_t * bytes) |
| { | { |
| Line 141 int get_redirect_rule( | Line 232 int get_redirect_rule( |
| return -1; | return -1; |
| if (ipfw_validate_ifname(ifname) < 0) | if (ipfw_validate_ifname(ifname) < 0) |
| return -1; | return -1; |
| if (timestamp) | |
| *timestamp = 0; | |
| do { | do { |
| count_rules = ipfw_fetch_ruleset(&rules, &total_rules, 10); | count_rules = ipfw_fetch_ruleset(&rules, &total_rules, 10); |
| if (count_rules < 0) | if (count_rules < 0) |
| Line 157 int get_redirect_rule( | Line 250 int get_redirect_rule( |
| *bytes = ptr->fw_bcnt; | *bytes = ptr->fw_bcnt; |
| if (iport != NULL) | if (iport != NULL) |
| *iport = ptr->fw_fwd_ip.sin_port; | *iport = ptr->fw_fwd_ip.sin_port; |
| if (desc != NULL && desclen > 0) | |
| strlcpy(desc, "", desclen); // TODO should we copy ptr->context? | |
| if (iaddr != NULL && iaddrlen > 0) { | if (iaddr != NULL && iaddrlen > 0) { |
| if (inet_ntop(AF_INET, &ptr->fw_out_if.fu_via_ip, iaddr, iaddrlen) == NULL) { | /* looks like fw_out_if.fu_via_ip is zero */ |
| //if (inet_ntop(AF_INET, &ptr->fw_out_if.fu_via_ip, iaddr, iaddrlen) == NULL) { | |
| if (inet_ntop(AF_INET, &ptr->fw_fwd_ip.sin_addr, iaddr, iaddrlen) == NULL) { | |
| syslog(LOG_ERR, "inet_ntop(): %m"); | syslog(LOG_ERR, "inet_ntop(): %m"); |
| goto error; | goto error; |
| } | } |
| } | } |
| if (rhost != NULL && rhostlen > 0) { | |
| if (ptr->fw_src.s_addr == 0) | |
| rhost[0] = '\0'; | |
| else if (inet_ntop(AF_INET, &ptr->fw_src.s_addr, rhost, rhostlen) == NULL) { | |
| syslog(LOG_ERR, "inet_ntop(): %m"); | |
| goto error; | |
| } | |
| } | |
| // And what if we found more than 1 matching rule? | // And what if we found more than 1 matching rule? |
| ipfw_free_ruleset(&rules); | ipfw_free_ruleset(&rules); |
| get_desc_time(eport, proto, desc, desclen, timestamp); | |
| return 0; | return 0; |
| } | } |
| } | } |
| Line 203 int delete_redirect_rule( | Line 305 int delete_redirect_rule( |
| goto error; | goto error; |
| // And what if we found more than 1 matching rule? | // And what if we found more than 1 matching rule? |
| ipfw_free_ruleset(&rules); | ipfw_free_ruleset(&rules); |
| del_desc_time(eport, proto); | |
| return 0; | return 0; |
| } | } |
| } | } |
| Line 215 error: | Line 318 error: |
| int add_filter_rule2( | int add_filter_rule2( |
| const char * ifname, | const char * ifname, |
| const char * rhost, | |
| const char * iaddr, | const char * iaddr, |
| unsigned short eport, | unsigned short eport, |
| unsigned short iport, | unsigned short iport, |
| int proto, | int proto, |
| const char * desc) | const char * desc) |
| { | { |
| return -1; | //return -1; |
| return 0; /* nothing to do, always success */ | |
| } | } |
| int delete_filter_rule( | int delete_filter_rule( |
| Line 229 int delete_filter_rule( | Line 334 int delete_filter_rule( |
| unsigned short eport, | unsigned short eport, |
| int proto) | int proto) |
| { | { |
| return -1; | //return -1; |
| return 0; /* nothing to do, always success */ | |
| } | } |
| int get_redirect_rule_by_index( | int get_redirect_rule_by_index( |
| Line 242 int get_redirect_rule_by_index( | Line 348 int get_redirect_rule_by_index( |
| int * proto, | int * proto, |
| char * desc, | char * desc, |
| int desclen, | int desclen, |
| char * rhost, | |
| int rhostlen, | |
| unsigned int * timestamp, | |
| u_int64_t * packets, | u_int64_t * packets, |
| u_int64_t * bytes) | u_int64_t * bytes) |
| { | { |
| Line 251 int get_redirect_rule_by_index( | Line 360 int get_redirect_rule_by_index( |
| if (index < 0) // TODO shouldn't we also validate the maximum? | if (index < 0) // TODO shouldn't we also validate the maximum? |
| return -1; | return -1; |
| if(timestamp) | |
| *timestamp = 0; | |
| ipfw_fetch_ruleset(&rules, &total_rules, index + 1); | ipfw_fetch_ruleset(&rules, &total_rules, index + 1); |
| if (total_rules == index + 1) { | if (total_rules > index) { |
| const struct ip_fw const * ptr = &rules[index]; | const struct ip_fw const * ptr = &rules[index]; |
| if (ptr->fw_prot == 0) // invalid rule | |
| goto error; | |
| if (proto != NULL) | if (proto != NULL) |
| *proto = ptr->fw_prot; | *proto = ptr->fw_prot; |
| if (eport != NULL) | if (eport != NULL) |
| Line 269 int get_redirect_rule_by_index( | Line 383 int get_redirect_rule_by_index( |
| *bytes = ptr->fw_bcnt; | *bytes = ptr->fw_bcnt; |
| if (iport != NULL) | if (iport != NULL) |
| *iport = ptr->fw_fwd_ip.sin_port; | *iport = ptr->fw_fwd_ip.sin_port; |
| if (desc != NULL && desclen > 0) | |
| strlcpy(desc, "", desclen); // TODO should we copy ptr->context? | |
| if (iaddr != NULL && iaddrlen > 0) { | if (iaddr != NULL && iaddrlen > 0) { |
| if (inet_ntop(AF_INET, &ptr->fw_out_if.fu_via_ip, iaddr, iaddrlen) == NULL) { | /* looks like fw_out_if.fu_via_ip is zero */ |
| //if (inet_ntop(AF_INET, &ptr->fw_out_if.fu_via_ip, iaddr, iaddrlen) == NULL) { | |
| if (inet_ntop(AF_INET, &ptr->fw_fwd_ip.sin_addr, iaddr, iaddrlen) == NULL) { | |
| syslog(LOG_ERR, "inet_ntop(): %m"); | syslog(LOG_ERR, "inet_ntop(): %m"); |
| goto error; | goto error; |
| } | } |
| } | } |
| if (rhost != NULL && rhostlen > 0) { | |
| if (ptr->fw_src.s_addr == 0) | |
| rhost[0] = '\0'; | |
| else if (inet_ntop(AF_INET, &ptr->fw_src.s_addr, rhost, rhostlen) == NULL) { | |
| syslog(LOG_ERR, "inet_ntop(): %m"); | |
| goto error; | |
| } | |
| } | |
| ipfw_free_ruleset(&rules); | ipfw_free_ruleset(&rules); |
| get_desc_time(*eport, *proto, desc, desclen, timestamp); | |
| return 0; | return 0; |
| } | } |
| Line 286 error: | Line 409 error: |
| ipfw_free_ruleset(&rules); | ipfw_free_ruleset(&rules); |
| return -1; | return -1; |
| } | } |
| /* upnp_get_portmappings_in_range() | |
| * return a list of all "external" ports for which a port | |
| * mapping exists */ | |
| unsigned short * | |
| get_portmappings_in_range(unsigned short startport, | |
| unsigned short endport, | |
| int proto, | |
| unsigned int * number) | |
| { | |
| unsigned short * array = NULL; | |
| unsigned int capacity = 128; | |
| int i, count_rules, total_rules = 0; | |
| struct ip_fw * rules = NULL; | |
| if (ipfw_validate_protocol(proto) < 0) | |
| return NULL; | |
| do { | |
| count_rules = ipfw_fetch_ruleset(&rules, &total_rules, 10); | |
| if (count_rules < 0) | |
| goto error; | |
| } while (count_rules == 10); | |
| array = calloc(capacity, sizeof(unsigned short)); | |
| if(!array) { | |
| syslog(LOG_ERR, "get_portmappings_in_range() : calloc error"); | |
| goto error; | |
| } | |
| *number = 0; | |
| for (i=0; i<total_rules-1; i++) { | |
| const struct ip_fw const * ptr = &rules[i]; | |
| unsigned short eport = ptr->fw_uar.fw_pts[0]; | |
| if (proto == ptr->fw_prot | |
| && startport <= eport | |
| && eport <= endport) { | |
| if(*number >= capacity) { | |
| capacity += 128; | |
| array = realloc(array, sizeof(unsigned short)*capacity); | |
| if(!array) { | |
| syslog(LOG_ERR, "get_portmappings_in_range() : realloc(%lu) error", sizeof(unsigned short)*capacity); | |
| *number = 0; | |
| goto error; | |
| } | |
| } | |
| array[*number] = eport; | |
| (*number)++; | |
| } | |
| } | |
| error: | |
| if (rules != NULL) | |
| ipfw_free_ruleset(&rules); | |
| return array; | |
| } | |