static void logVal( const char * func, int ret ) { if( ret == NATPMP_TRYAGAIN ) return; if( ret >= 0 ) tr_ninf( getKey( ), _( "%s succeeded (%d)" ), func, ret ); else tr_ndbg( getKey( ), "%s failed. Natpmp returned %d (%s); errno is %d (%s)", func, ret, strnatpmperr( ret ), errno, tr_strerror( errno ) ); }
/* sample code for using libnatpmp */ int main(int argc, char * * argv) { natpmp_t natpmp; natpmpresp_t response; int r; int sav_errno; struct timeval timeout; fd_set fds; int i; int protocol = 0; uint16_t privateport = 0; uint16_t publicport = 0; uint32_t lifetime = 3600; int command = 0; int forcegw = 0; in_addr_t gateway = 0; struct in_addr gateway_in_use; #ifdef WIN32 WSADATA wsaData; int nResult = WSAStartup(MAKEWORD(2,2), &wsaData); if(nResult != NO_ERROR) { fprintf(stderr, "WSAStartup() failed.\n"); return -1; } #endif /* argument parsing */ for(i=1; i<argc; i++) { if(argv[i][0] == '-') { switch(argv[i][1]) { case 'h': usage(stdout, argv[0]); return 0; case 'g': forcegw = 1; if(argc < i + 1) { fprintf(stderr, "Not enough arguments for option -%c\n", argv[i][1]); return 1; } gateway = inet_addr(argv[++i]); break; case 'a': command = 'a'; if(argc < i + 4) { fprintf(stderr, "Not enough arguments for option -%c\n", argv[i][1]); return 1; } i++; if(1 != sscanf(argv[i], "%hu", &publicport)) { fprintf(stderr, "%s is not a correct 16bits unsigned integer\n", argv[i]); return 1; } i++; if(1 != sscanf(argv[i], "%hu", &privateport)) { fprintf(stderr, "%s is not a correct 16bits unsigned integer\n", argv[i]); return 1; } i++; if(0 == strcasecmp(argv[i], "tcp")) protocol = NATPMP_PROTOCOL_TCP; else if(0 == strcasecmp(argv[i], "udp")) protocol = NATPMP_PROTOCOL_UDP; else { fprintf(stderr, "%s is not a valid protocol\n", argv[i]); return 1; } if(argc > i + 1) { if(1 != sscanf(argv[i+1], "%u", &lifetime)) { fprintf(stderr, "%s is not a correct 32bits unsigned integer\n", argv[i]); } else { i++; } } break; default: fprintf(stderr, "Unknown option %s\n", argv[i]); usage(stderr, argv[0]); return 1; } } else { fprintf(stderr, "Unknown option %s\n", argv[i]); usage(stderr, argv[0]); return 1; } } /* initnatpmp() */ r = initnatpmp(&natpmp, forcegw, gateway); printf("initnatpmp() returned %d (%s)\n", r, r?"FAILED":"SUCCESS"); if(r<0) return 1; gateway_in_use.s_addr = natpmp.gateway; printf("using gateway : %s\n", inet_ntoa(gateway_in_use)); /* sendpublicaddressrequest() */ r = sendpublicaddressrequest(&natpmp); printf("sendpublicaddressrequest returned %d (%s)\n", r, r==2?"SUCCESS":"FAILED"); if(r<0) return 1; do { FD_ZERO(&fds); FD_SET(natpmp.s, &fds); getnatpmprequesttimeout(&natpmp, &timeout); r = select(FD_SETSIZE, &fds, NULL, NULL, &timeout); if(r<0) { fprintf(stderr, "select()"); return 1; } r = readnatpmpresponseorretry(&natpmp, &response); sav_errno = errno; printf("readnatpmpresponseorretry returned %d (%s)\n", r, r==0?"OK":(r==NATPMP_TRYAGAIN?"TRY AGAIN":"FAILED")); if(r<0 && r!=NATPMP_TRYAGAIN) { #ifdef ENABLE_STRNATPMPERR fprintf(stderr, "readnatpmpresponseorretry() failed : %s\n", strnatpmperr(r)); #endif fprintf(stderr, " errno=%d '%s'\n", sav_errno, strerror(sav_errno)); } } while(r==NATPMP_TRYAGAIN); if(r<0) return 1; /* TODO : check that response.type == 0 */ printf("Public IP address : %s\n", inet_ntoa(response.pnu.publicaddress.addr)); printf("epoch = %u\n", response.epoch); if(command == 'a') { /* sendnewportmappingrequest() */ r = sendnewportmappingrequest(&natpmp, protocol, privateport, publicport, lifetime); printf("sendnewportmappingrequest returned %d (%s)\n", r, r==12?"SUCCESS":"FAILED"); if(r < 0) return 1; do { FD_ZERO(&fds); FD_SET(natpmp.s, &fds); getnatpmprequesttimeout(&natpmp, &timeout); select(FD_SETSIZE, &fds, NULL, NULL, &timeout); r = readnatpmpresponseorretry(&natpmp, &response); printf("readnatpmpresponseorretry returned %d (%s)\n", r, r==0?"OK":(r==NATPMP_TRYAGAIN?"TRY AGAIN":"FAILED")); } while(r==NATPMP_TRYAGAIN); if(r<0) { #ifdef ENABLE_STRNATPMPERR fprintf(stderr, "readnatpmpresponseorretry() failed : %s\n", strnatpmperr(r)); #endif return 1; } printf("Mapped public port %hu protocol %s to local port %hu " "liftime %u\n", response.pnu.newportmapping.mappedpublicport, response.type == NATPMP_RESPTYPE_UDPPORTMAPPING ? "UDP" : (response.type == NATPMP_RESPTYPE_TCPPORTMAPPING ? "TCP" : "UNKNOWN"), response.pnu.newportmapping.privateport, response.pnu.newportmapping.lifetime); printf("epoch = %u\n", response.epoch); } r = closenatpmp(&natpmp); printf("closenatpmp() returned %d (%s)\n", r, r==0?"SUCCESS":"FAILED"); if(r<0) return 1; return 0; }
int natpmp_handler(struct natpmp_handle_t *handle, uint16_t port, time_t lifespan, time_t now) { natpmpresp_t response; // Retry later if we want to wait longer if (handle->retry > now) { return PF_RETRY; } #ifdef DEBUG log_debug("NAT-PMP: Handle port: %hu, lifespan: %ld, state: %s", port, lifespan, natpmp_statestr(handle->state)); #endif // Initialize data structure / socket if (handle->state == NATPMP_STATE_INIT) { int rc = initnatpmp(&handle->natpmp, 0, 0); if (rc >= 0) { handle->state = NATPMP_STATE_REQUEST_GATEWAY; return PF_RETRY; } else { log_debug("NAT-PMP: Method initnatpmp returned %d.", rc); goto error; } } // Request gateway address if (handle->state == NATPMP_STATE_REQUEST_GATEWAY) { int rc = sendpublicaddressrequest(&handle->natpmp); if (rc >= 0) { handle->retry = now + 8; handle->state = NATPMP_STATE_RECEIVE_GATEWAY; return PF_RETRY; } else { log_debug("NAT-PMP: Method sendpublicaddressrequest returned %d.", rc); goto error; } } // Read public gateway address if (handle->state == NATPMP_STATE_RECEIVE_GATEWAY) { int rc = readnatpmpresponseorretry(&handle->natpmp, &response); if (rc >= 0) { char str[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &response.pnu.publicaddress.addr, str, sizeof (str)); log_info("NAT-PMP: Found public address \"%s\".", str); handle->state = NATPMP_STATE_REQUEST_PORTMAPPING; return PF_RETRY; } else if (rc == NATPMP_TRYAGAIN) { handle->retry = now + (10 * 60); handle->state = NATPMP_STATE_REQUEST_GATEWAY; return PF_RETRY; } else { log_debug("NAT-PMP: Method readnatpmpresponseorretry returned %d.", rc); goto error; } } // Add/Remove port mappings if (handle->state == NATPMP_STATE_REQUEST_PORTMAPPING) { int rc_udp = sendnewportmappingrequest(&handle->natpmp, NATPMP_PROTOCOL_UDP, port, port, lifespan); int rc_tcp = sendnewportmappingrequest(&handle->natpmp, NATPMP_PROTOCOL_TCP, port, port, lifespan); if (rc_udp >= 0 && rc_tcp >= 0) { handle->retry = now + 2; handle->state = NATPMP_STATE_RECEIVE_PORTMAPPING; return PF_RETRY; } else { log_debug("NAT-PMP: Method sendnewportmappingrequest returned an error: %s", strnatpmperr((rc_udp >= 0) ? rc_tcp : rc_udp)); goto error; } } // Check port mapping if (handle->state == NATPMP_STATE_RECEIVE_PORTMAPPING) { int rc = readnatpmpresponseorretry(&handle->natpmp, &response); if (rc >= 0) { int private_port = response.pnu.newportmapping.privateport; int public_port = response.pnu.newportmapping.mappedpublicport; time_t lifetime = response.pnu.newportmapping.lifetime; if (lifetime > 0) { log_info("NAT-PMP: Port forwarding added for port %d (to private port %d) for %ld seconds.", public_port, private_port, lifetime); handle->state = NATPMP_STATE_REQUEST_PORTMAPPING; return PF_DONE; } else { log_debug("NAT-PMP: Port forwarding removed for public port %d (to private port %d) for %ld seconds.", public_port, private_port, lifetime); handle->state = NATPMP_STATE_REQUEST_PORTMAPPING; return PF_DONE; } } else if (rc == NATPMP_TRYAGAIN) { handle->state = NATPMP_STATE_RECEIVE_PORTMAPPING; return PF_RETRY; } else { log_debug("NAT-PMP: Port forwarding failed for port %d.", port); goto error; } } error:; handle->retry = now + 60; handle->state = NATPMP_STATE_ERROR; return PF_ERROR; }