/* Firewall independant code which call the FW dependant code. */ int upnp_get_redirection_infos(unsigned short eport, const char * protocol, unsigned short * iport, char * iaddr, int iaddrlen, char * desc, int desclen, char * rhost, int rhostlen, unsigned int * leaseduration) { int r; unsigned int timestamp; time_t current_time; if(desc && (desclen > 0)) desc[0] = '\0'; if(rhost && (rhostlen > 0)) rhost[0] = '\0'; r = get_redirect_rule(ext_if_name, eport, proto_atoi(protocol), iaddr, iaddrlen, iport, desc, desclen, rhost, rhostlen, ×tamp, 0, 0); if(r == 0 && timestamp > 0 && timestamp > (unsigned int)(current_time = time(NULL))) { *leaseduration = timestamp - current_time; } else { *leaseduration = 0; } return r; }
/* upnp_redirect() * calls OS/fw dependant implementation of the redirection. * protocol should be the string "TCP" or "UDP" * returns: 0 on success * -1 failed to redirect * -2 already redirected * -3 permission check failed */ int upnp_redirect(const char * rhost, unsigned short eport, const char * iaddr, unsigned short iport, const char * protocol, const char * desc, unsigned int leaseduration) { int proto, r; char iaddr_old[32]; unsigned short iport_old; struct in_addr address; unsigned int timestamp; proto = proto_atoi(protocol); if(inet_aton(iaddr, &address) < 0) { syslog(LOG_ERR, "inet_aton(%s) : %m", iaddr); return -1; } if(!check_upnp_rule_against_permissions(upnppermlist, num_upnpperm, eport, address, iport)) { syslog(LOG_INFO, "redirection permission check failed for " "%hu->%s:%hu %s", eport, iaddr, iport, protocol); return -3; } r = get_redirect_rule(ext_if_name, eport, proto, iaddr_old, sizeof(iaddr_old), &iport_old, 0, 0, 0, 0, ×tamp, 0, 0); if(r == 0) { /* if existing redirect rule matches redirect request return success * xbox 360 does not keep track of the port it redirects and will * redirect another port when receiving ConflictInMappingEntry */ if(strcmp(iaddr, iaddr_old)==0 && iport==iport_old) { /* redirection allready exists */ syslog(LOG_INFO, "port %hu %s already redirected to %s:%hu, replacing", eport, (proto==IPPROTO_TCP)?"tcp":"udp", iaddr_old, iport_old); /* remove and then add again */ if(_upnp_delete_redir(eport, proto) < 0) { syslog(LOG_ERR, "failed to remove port mapping"); return 0; } } else { syslog(LOG_INFO, "port %hu protocol %s already redirected to %s:%hu", eport, protocol, iaddr_old, iport_old); return -2; } #ifdef CHECK_PORTINUSE } else if (port_in_use(ext_if_name, eport, proto, iaddr, iport) > 0) { syslog(LOG_INFO, "port %hu protocol %s already in use", eport, protocol); return -2; #endif /* CHECK_PORTINUSE */ } timestamp = (leaseduration > 0) ? time(NULL) + leaseduration : 0; syslog(LOG_INFO, "redirecting port %hu to %s:%hu protocol %s for: %s", eport, iaddr, iport, protocol, desc); return upnp_redirect_internal(rhost, eport, iaddr, iport, proto, desc, timestamp); }
int upnp_get_redirection_infos(unsigned short eport, const char * protocol, unsigned short * iport, char * iaddr, int iaddrlen, char * desc, int desclen) { if(desc && (desclen > 0)) desc[0] = '\0'; return get_redirect_rule(ext_if_name, eport, proto_atoi(protocol), iaddr, iaddrlen, iport, desc, desclen, 0, 0); }
/* upnp_get_portmappings_in_range() * return a list of all "external" ports for which a port * mapping exists */ unsigned short * upnp_get_portmappings_in_range(unsigned short startport, unsigned short endport, const char * protocol, unsigned int * number) { int proto; proto = proto_atoi(protocol); if(!number) return NULL; return get_portmappings_in_range(startport, endport, proto, number); }
/* upnp_redirect() * calls OS/fw dependant implementation of the redirection. * protocol should be the string "TCP" or "UDP" * returns: 0 on success * -1 failed to redirect * -2 already redirected * -3 permission check failed */ int upnp_redirect(const char * rhost, unsigned short eport, const char * iaddr, unsigned short iport, const char * protocol, const char * desc, unsigned int leaseduration) { int proto, r; char iaddr_old[32]; unsigned short iport_old; struct in_addr address; unsigned int timestamp; proto = proto_atoi(protocol); if(inet_aton(iaddr, &address) < 0) { syslog(LOG_ERR, "inet_aton(%s) : %m", iaddr); return -1; } if(!check_upnp_rule_against_permissions(upnppermlist, num_upnpperm, eport, address, iport)) { syslog(LOG_INFO, "redirection permission check failed for " "%hu->%s:%hu %s", eport, iaddr, iport, protocol); return -3; } r = get_redirect_rule(ext_if_name, eport, proto, iaddr_old, sizeof(iaddr_old), &iport_old, 0, 0, 0, 0, ×tamp, 0, 0); if(r == 0) { /* if existing redirect rule matches redirect request return success * xbox 360 does not keep track of the port it redirects and will * redirect another port when receiving ConflictInMappingEntry */ if(strcmp(iaddr, iaddr_old)==0 && iport==iport_old) { syslog(LOG_INFO, "ignoring redirect request as it matches existing redirect"); } else { syslog(LOG_INFO, "port %hu protocol %s already redirected to %s:%hu", eport, protocol, iaddr_old, iport_old); return -2; } } else { timestamp = (leaseduration > 0) ? time(NULL) + leaseduration : 0; syslog(LOG_INFO, "redirecting port %hu to %s:%hu protocol %s for: %s", eport, iaddr, iport, protocol, desc); return upnp_redirect_internal(rhost, eport, iaddr, iport, proto, desc, timestamp); } return 0; }
int upnp_delete_redirection(unsigned short eport, const char * protocol) { syslog(LOG_INFO, "removing redirect rule port %hu %s", eport, protocol); return _upnp_delete_redir(eport, proto_atoi(protocol)); }
/* reload_from_lease_file() * read lease_file and add the rules contained */ int reload_from_lease_file() { FILE * fd; char * p; unsigned short eport, iport; char * proto; char * iaddr; char * desc; char * rhost; unsigned int leaseduration; unsigned int timestamp; time_t current_time; char line[128]; int r; if(!lease_file) return -1; fd = fopen( lease_file, "r"); if (fd==NULL) { syslog(LOG_DEBUG, "could not open lease file: %s", lease_file); return -1; } if(unlink(lease_file) < 0) { syslog(LOG_WARNING, "could not unlink file %s : %m", lease_file); } current_time = time(NULL); while(fgets(line, sizeof(line), fd)) { syslog(LOG_DEBUG, "parsing lease file line '%s'", line); proto = line; p = strchr(line, ':'); if(!p) { syslog(LOG_ERR, "unrecognized data in lease file"); continue; } *(p++) = '\0'; iaddr = strchr(p, ':'); if(!iaddr) { syslog(LOG_ERR, "unrecognized data in lease file"); continue; } *(iaddr++) = '\0'; eport = (unsigned short)atoi(p); p = strchr(iaddr, ':'); if(!p) { syslog(LOG_ERR, "unrecognized data in lease file"); continue; } *(p++) = '\0'; iport = (unsigned short)atoi(p); p = strchr(p, ':'); if(!p) { syslog(LOG_ERR, "unrecognized data in lease file"); continue; } *(p++) = '\0'; desc = strchr(p, ':'); if(!desc) { syslog(LOG_ERR, "unrecognized data in lease file"); continue; } *(desc++) = '\0'; /*timestamp = (unsigned int)atoi(p);*/ timestamp = (unsigned int)strtoul(p, NULL, 10); /* trim description */ while(isspace(*desc)) desc++; p = desc; while(*(p+1)) p++; while(isspace(*p) && (p > desc)) *(p--) = '\0'; if(timestamp > 0) { if(timestamp <= (unsigned int)current_time) { syslog(LOG_NOTICE, "already expired lease in lease file"); continue; } else { leaseduration = timestamp - current_time; } } else { leaseduration = 0; /* default value */ } rhost = NULL; r = upnp_redirect(rhost, eport, iaddr, iport, proto, desc, leaseduration); if(r == -1) { syslog(LOG_ERR, "Failed to redirect %hu -> %s:%hu protocol %s", eport, iaddr, iport, proto); } else if(r == -2) { /* Add the redirection again to the lease file */ lease_file_add(eport, iaddr, iport, proto_atoi(proto), desc, timestamp); } } fclose(fd); return 0; }
/* upnp_redirect() * calls OS/fw dependant implementation of the redirection. * protocol should be the string "TCP" or "UDP" * returns: 0 on success * -1 failed to redirect * -2 already redirected * -3 permission check failed * -4 already redirected (other mechanism) */ int upnp_redirect(const char * rhost, unsigned short eport, const char * iaddr, unsigned short iport, const char * protocol, const char * desc, unsigned int leaseduration) { int proto, r; char iaddr_old[32]; char rhost_old[32]; unsigned short iport_old; struct in_addr address; unsigned int timestamp; proto = proto_atoi(protocol); if(inet_aton(iaddr, &address) <= 0) { syslog(LOG_ERR, "inet_aton(%s) FAILED", iaddr); return -1; } if(!check_upnp_rule_against_permissions(upnppermlist, num_upnpperm, eport, address, iport)) { syslog(LOG_INFO, "redirection permission check failed for " "%hu->%s:%hu %s", eport, iaddr, iport, protocol); return -3; } /* IGDv1 (WANIPConnection:1 Service Template Version 1.01 / Nov 12, 2001) * - 2.2.20.PortMappingDescription : * Overwriting Previous / Existing Port Mappings: * If the RemoteHost, ExternalPort, PortMappingProtocol and InternalClient * are exactly the same as an existing mapping, the existing mapping values * for InternalPort, PortMappingDescription, PortMappingEnabled and * PortMappingLeaseDuration are overwritten. * Rejecting a New Port Mapping: * In cases where the RemoteHost, ExternalPort and PortMappingProtocol * are the same as an existing mapping, but the InternalClient is * different, the action is rejected with an appropriate error. * Add or Reject New Port Mapping behavior based on vendor implementation: * In cases where the ExternalPort, PortMappingProtocol and InternalClient * are the same, but RemoteHost is different, the vendor can choose to * support both mappings simultaneously, or reject the second mapping * with an appropriate error. * * - 2.4.16.AddPortMapping * This action creates a new port mapping or overwrites an existing * mapping with the same internal client. If the ExternalPort and * PortMappingProtocol pair is already mapped to another internal client, * an error is returned. * * IGDv2 (WANIPConnection:2 Service Standardized DCP (SDCP) Sep 10, 2010) * Protocol ExternalPort RemoteHost InternalClient Result * = = ≠ ≠ Failure * = = ≠ = Failure or success * (vendor specific) * = = = ≠ Failure * = = = = Success (overwrite) */ rhost_old[0] = '\0'; r = get_redirect_rule(ext_if_name, eport, proto, iaddr_old, sizeof(iaddr_old), &iport_old, 0, 0, rhost_old, sizeof(rhost_old), ×tamp, 0, 0); if(r == 0) { if(strcmp(iaddr, iaddr_old)==0 && ((rhost == NULL && rhost_old[0]=='\0') || (rhost && (strcmp(rhost, "*") == 0) && rhost_old[0]=='\0') || (rhost && (strcmp(rhost, rhost_old) == 0)))) { syslog(LOG_INFO, "updating existing port mapping %hu %s (rhost '%s') => %s:%hu", eport, protocol, rhost_old, iaddr_old, iport_old); timestamp = (leaseduration > 0) ? time(NULL) + leaseduration : 0; if(iport != iport_old) { r = update_portmapping(ext_if_name, eport, proto, iport, desc, timestamp); } else { r = update_portmapping_desc_timestamp(ext_if_name, eport, proto, desc, timestamp); } #ifdef ENABLE_LEASEFILE if(r == 0) { lease_file_remove(eport, proto); lease_file_add(eport, iaddr, iport, proto, desc, timestamp); } #endif /* ENABLE_LEASEFILE */ return r; } else { syslog(LOG_INFO, "port %hu %s (rhost '%s') already redirected to %s:%hu", eport, protocol, rhost_old, iaddr_old, iport_old); return -2; } #ifdef CHECK_PORTINUSE } else if (port_in_use(ext_if_name, eport, proto, iaddr, iport) > 0) { syslog(LOG_INFO, "port %hu protocol %s already in use", eport, protocol); return -4; #endif /* CHECK_PORTINUSE */ } else { timestamp = (leaseduration > 0) ? time(NULL) + leaseduration : 0; syslog(LOG_INFO, "redirecting port %hu to %s:%hu protocol %s for: %s", eport, iaddr, iport, protocol, desc); return upnp_redirect_internal(rhost, eport, iaddr, iport, proto, desc, timestamp); } }
int upnp_delete_redirection(unsigned short eport, const char * protocol) { NP_UPNP_DEBUG("removing redirect rule port %hu %s\n", eport, protocol); return _upnp_delete_redir(eport, proto_atoi(protocol)); }
/* upnp_redirect() * calls OS/fw dependant implementation of the redirection. * protocol should be the string "TCP" or "UDP" * returns: 0 on success * -1 failed to redirect * -2 already redirected * -3 permission check failed */ int upnp_redirect(const char * rhost, unsigned short eport, const char * iaddr, unsigned short iport, const char * protocol, const char * desc, unsigned int leaseduration, int enabled) { int proto, r; int rr = 0; char iaddr_old[32]; int enabled_old; unsigned short iport_old; struct in_addr address; unsigned int timestamp; NP_UPNP_DEBUG("enter upnp_redirect, enabled arg is %d\n", enabled); proto = proto_atoi(protocol); if(inet_aton(iaddr, &address) < 0) { NP_UPNP_ERROR("inet_aton(%s) : %s\n", iaddr, strerror(errno)); return -1; } if(!check_upnp_rule_against_permissions(upnppermlist, num_upnpperm, eport, address, iport)) { NP_UPNP_DEBUG("redirection permission check failed for " "%hu->%s:%hu %s\n", eport, iaddr, iport, protocol); return -3; } enabled_old = enabled; r = get_redirect_rule(ext_if_name, eport, proto, iaddr_old, sizeof(iaddr_old), &iport_old, 0, 0, 0, 0, ×tamp, 0, 0, &enabled_old); if(r == 0) { /* if existing redirect rule matches redirect request return success * xbox 360 does not keep track of the port it redirects and will * redirect another port when receiving ConflictInMappingEntry */ if(strcmp(iaddr, iaddr_old)==0 && iport==iport_old) { if(enabled_old != enabled) { reload_port_mapping_rules(); } else { NP_UPNP_DEBUG("ignoring redirect request as it matches existing redirect\n"); } } else { NP_UPNP_DEBUG("port %hu protocol %s already redirected to %s:%hu\n", eport, protocol, iaddr_old, iport_old); /* 删除已存在的规则 */ upnp_delete_redirection(eport, protocol); /* 重新添加规则 */ timestamp = (leaseduration > 0) ? time(NULL) + leaseduration : 0; NP_UPNP_DEBUG("redirecting port %hu to %s:%hu protocol %s for: %s\n", eport, iaddr, iport, protocol, desc); rr = upnp_redirect_internal(rhost, eport, iaddr, iport, proto, desc, timestamp, enabled); NP_UPNP_DEBUG("upnp_redirect_internal(...) returns %d\n", rr); return rr; } } else { timestamp = (leaseduration > 0) ? time(NULL) + leaseduration : 0; NP_UPNP_DEBUG("redirecting port %hu to %s:%hu protocol %s for: %s\n", eport, iaddr, iport, protocol, desc); rr = upnp_redirect_internal(rhost, eport, iaddr, iport, proto, desc, timestamp, enabled); NP_UPNP_DEBUG("upnp_redirect_internal(...) returns %d\n", rr); return rr; } return 0; }
/* upnp_redirect() * calls OS/fw dependant implementation of the redirection. * protocol should be the string "TCP" or "UDP" * returns: 0 on success * -1 failed to redirect * -2 already redirected * -3 permission check failed */ int upnp_redirect(unsigned short eport, const char * iaddr, unsigned short iport, const char * protocol, const char * desc) { int proto, r; char iaddr_old[32]; unsigned short iport_old; struct in_addr address; proto = proto_atoi(protocol); if(inet_aton(iaddr, &address) < 0) { syslog(LOG_ERR, "inet_aton(%s) : %m", iaddr); return -1; } if(!check_upnp_rule_against_permissions(upnppermlist, num_upnpperm, eport, address, iport)) { syslog(LOG_INFO, "redirection permission check failed for " "%hu->%s:%hu %s", eport, iaddr, iport, protocol); return -3; } r = get_redirect_rule(ext_if_name, eport, proto, iaddr_old, sizeof(iaddr_old), &iport_old, 0, 0, 0, 0); if(r == 0) { /* if existing redirect rule matches redirect request return success * xbox 360 does not keep track of the port it redirects and will * redirect another port when receiving ConflictInMappingEntry */ if(strcmp(iaddr,iaddr_old)==0 && iport==iport_old) { syslog(LOG_INFO, "ignoring redirect request as it matches existing redirect"); } else { syslog(LOG_INFO, "port %hu protocol %s already redirected to %s:%hu", eport, protocol, iaddr_old, iport_old); return -2; } } else { syslog(LOG_INFO, "redirecting port %hu to %s:%hu protocol %s for: %s", eport, iaddr, iport, protocol, desc); return upnp_redirect_internal(eport, iaddr, iport, proto, desc); #if 0 if(add_redirect_rule2(ext_if_name, eport, iaddr, iport, proto, desc) < 0) { return -1; } syslog(LOG_INFO, "creating pass rule to %s:%hu protocol %s for: %s", iaddr, iport, protocol, desc); if(add_filter_rule2(ext_if_name, iaddr, eport, iport, proto, desc) < 0) { /* clean up the redirect rule */ #if !defined(__linux__) delete_redirect_rule(ext_if_name, eport, proto); #endif return -1; } #endif } return 0; }