#include #include #include #include #include #include #include #include #include "globals.h" #include "gatedevice.h" #include "pmlist.h" #include "util.h" //Definitions for mapping expiration timer thread static TimerThread gExpirationTimerThread; static ThreadPool gExpirationThreadPool; // MUTEX for locking shared state variables whenver they are changed static ithread_mutex_t DevMutex = PTHREAD_MUTEX_INITIALIZER; // Main event handler for callbacks from the SDK. Determine type of event // and dispatch to the appropriate handler (Note: Get Var Request deprecated int EventHandler(Upnp_EventType EventType, void *Event, void *Cookie) { switch (EventType) { case UPNP_EVENT_SUBSCRIPTION_REQUEST: HandleSubscriptionRequest((struct Upnp_Subscription_Request *) Event); break; // -- Deprecated -- case UPNP_CONTROL_GET_VAR_REQUEST: HandleGetVarRequest((struct Upnp_State_Var_Request *) Event); break; case UPNP_CONTROL_ACTION_REQUEST: HandleActionRequest((struct Upnp_Action_Request *) Event); break; default: trace(1, "Error in EventHandler: Unknown event type %d", EventType); } return (0); } // Grab our UDN from the Description Document. This may not be needed, // the UDN comes with the request, but we leave this for other device initializations int StateTableInit(char *descDocUrl) { IXML_Document *ixmlDescDoc; int ret; if ((ret = UpnpDownloadXmlDoc(descDocUrl, &ixmlDescDoc)) != UPNP_E_SUCCESS) { syslog(LOG_ERR, "Could not parse description document. Exiting ..."); UpnpFinish(); exit(0); } // Get the UDN from the description document, then free the DescDoc's memory gateUDN = GetFirstDocumentItem(ixmlDescDoc, "UDN"); ixmlDocument_free(ixmlDescDoc); // Initialize our linked list of port mappings. pmlist_Head = pmlist_Current = NULL; PortMappingNumberOfEntries = 0; return (ret); } // Handles subscription request for state variable notifications int HandleSubscriptionRequest(struct Upnp_Subscription_Request *sr_event) { IXML_Document *propSet = NULL; ithread_mutex_lock(&DevMutex); if (strcmp(sr_event->UDN, gateUDN) == 0) { // WAN Common Interface Config Device Notifications if (strcmp(sr_event->ServiceId, "urn:upnp-org:serviceId:WANCommonIFC1") == 0) { trace(3, "Recieved request to subscribe to WANCommonIFC1"); UpnpAddToPropertySet(&propSet, "PhysicalLinkStatus", "Up"); UpnpAcceptSubscriptionExt(deviceHandle, sr_event->UDN, sr_event->ServiceId, propSet, sr_event->Sid); ixmlDocument_free(propSet); } // WAN IP Connection Device Notifications else if (strcmp(sr_event->ServiceId, "urn:upnp-org:serviceId:WANIPConn1") == 0) { GetIpAddressStr(ExternalIPAddress, g_vars.extInterfaceName); trace(3, "Received request to subscribe to WANIPConn1"); UpnpAddToPropertySet(&propSet, "PossibleConnectionTypes","IP_Routed"); UpnpAddToPropertySet(&propSet, "ConnectionStatus","Connected"); UpnpAddToPropertySet(&propSet, "ExternalIPAddress", ExternalIPAddress); UpnpAddToPropertySet(&propSet, "PortMappingNumberOfEntries","0"); UpnpAcceptSubscriptionExt(deviceHandle, sr_event->UDN, sr_event->ServiceId, propSet, sr_event->Sid); ixmlDocument_free(propSet); } } ithread_mutex_unlock(&DevMutex); return(1); } int HandleGetVarRequest(struct Upnp_State_Var_Request *gv_request) { // GET VAR REQUEST DEPRECATED FROM UPnP SPECIFICATIONS // Report this in debug and ignore requests. If anyone experiences problems // please let us know. trace(3, "Deprecated Get Variable Request received. Ignoring."); return 1; } int HandleActionRequest(struct Upnp_Action_Request *ca_event) { int result = 0; ithread_mutex_lock(&DevMutex); if (strcmp(ca_event->DevUDN, gateUDN) == 0) { // Common debugging info, hopefully gets removed soon. trace(3, "ActionName = %s", ca_event->ActionName); if (strcmp(ca_event->ServiceID, "urn:upnp-org:serviceId:WANIPConn1") == 0) { if (strcmp(ca_event->ActionName,"GetConnectionTypeInfo") == 0) result = GetConnectionTypeInfo(ca_event); else if (strcmp(ca_event->ActionName,"GetNATRSIPStatus") == 0) result = GetNATRSIPStatus(ca_event); else if (strcmp(ca_event->ActionName,"SetConnectionType") == 0) result = SetConnectionType(ca_event); else if (strcmp(ca_event->ActionName,"RequestConnection") == 0) result = RequestConnection(ca_event); else if (strcmp(ca_event->ActionName,"AddPortMapping") == 0) result = AddPortMapping(ca_event); else if (strcmp(ca_event->ActionName,"GetGenericPortMappingEntry") == 0) result = GetGenericPortMappingEntry(ca_event); else if (strcmp(ca_event->ActionName,"GetSpecificPortMappingEntry") == 0) result = GetSpecificPortMappingEntry(ca_event); else if (strcmp(ca_event->ActionName,"GetExternalIPAddress") == 0) result = GetExternalIPAddress(ca_event); else if (strcmp(ca_event->ActionName,"DeletePortMapping") == 0) result = DeletePortMapping(ca_event); else if (strcmp(ca_event->ActionName,"GetStatusInfo") == 0) result = GetStatusInfo(ca_event); // Intentionally Non-Implemented Functions -- To be added later /*else if (strcmp(ca_event->ActionName,"RequestTermination") == 0) result = RequestTermination(ca_event); else if (strcmp(ca_event->ActionName,"ForceTermination") == 0) result = ForceTermination(ca_event); else if (strcmp(ca_event->ActionName,"SetAutoDisconnectTime") == 0) result = SetAutoDisconnectTime(ca_event); else if (strcmp(ca_event->ActionName,"SetIdleDisconnectTime") == 0) result = SetIdleDisconnectTime(ca_event); else if (strcmp(ca_event->ActionName,"SetWarnDisconnectDelay") == 0) result = SetWarnDisconnectDelay(ca_event); else if (strcmp(ca_event->ActionName,"GetAutoDisconnectTime") == 0) result = GetAutoDisconnectTime(ca_event); else if (strcmp(ca_event->ActionName,"GetIdleDisconnectTime") == 0) result = GetIdleDisconnectTime(ca_event); else if (strcmp(ca_event->ActionName,"GetWarnDisconnectDelay") == 0) result = GetWarnDisconnectDelay(ca_event);*/ else result = InvalidAction(ca_event); } else if (strcmp(ca_event->ServiceID,"urn:upnp-org:serviceId:WANCommonIFC1") == 0) { if (strcmp(ca_event->ActionName,"GetTotalBytesSent") == 0) result = GetTotal(ca_event, STATS_TX_BYTES); else if (strcmp(ca_event->ActionName,"GetTotalBytesReceived") == 0) result = GetTotal(ca_event, STATS_RX_BYTES); else if (strcmp(ca_event->ActionName,"GetTotalPacketsSent") == 0) result = GetTotal(ca_event, STATS_TX_PACKETS); else if (strcmp(ca_event->ActionName,"GetTotalPacketsReceived") == 0) result = GetTotal(ca_event, STATS_RX_PACKETS); else if (strcmp(ca_event->ActionName,"GetCommonLinkProperties") == 0) result = GetCommonLinkProperties(ca_event); else { trace(1, "Invalid Action Request : %s",ca_event->ActionName); result = InvalidAction(ca_event); } } } ithread_mutex_unlock(&DevMutex); return (result); } // Default Action when we receive unknown Action Requests int InvalidAction(struct Upnp_Action_Request *ca_event) { ca_event->ErrCode = 401; strcpy(ca_event->ErrStr, "Invalid Action"); ca_event->ActionResult = NULL; return (ca_event->ErrCode); } // As IP_Routed is the only relevant Connection Type for Linux-IGD // we respond with IP_Routed as both current type and only type int GetConnectionTypeInfo (struct Upnp_Action_Request *ca_event) { char resultStr[RESULT_LEN]; IXML_Document *result; snprintf(resultStr, RESULT_LEN, "\n" "IP_Routed\n" "IP_Routed" ""); // Create a IXML_Document from resultStr and return with ca_event if ((result = ixmlParseBuffer(resultStr)) != NULL) { ca_event->ActionResult = result; ca_event->ErrCode = UPNP_E_SUCCESS; } else { trace(1, "Error parsing Response to GetConnectionTypeinfo: %s", resultStr); ca_event->ActionResult = NULL; ca_event->ErrCode = 402; } return(ca_event->ErrCode); } // Linux-IGD does not support RSIP. However NAT is of course // so respond with NewNATEnabled = 1 int GetNATRSIPStatus(struct Upnp_Action_Request *ca_event) { char resultStr[RESULT_LEN]; IXML_Document *result; snprintf(resultStr, RESULT_LEN, "\n" "0\n" "1\n" ""); // Create a IXML_Document from resultStr and return with ca_event if ((result = ixmlParseBuffer(resultStr)) != NULL) { ca_event->ActionResult = result; ca_event->ErrCode = UPNP_E_SUCCESS; } else { trace(1, "Error parsing Response to GetNATRSIPStatus: %s", resultStr); ca_event->ActionResult = NULL; ca_event->ErrCode = 402; } return(ca_event->ErrCode); } // Connection Type is a Read Only Variable as linux-igd is only // a device that supports a NATing IP router (not an Ethernet // bridge). Possible other uses may be explored. int SetConnectionType(struct Upnp_Action_Request *ca_event) { // Ignore requests ca_event->ActionResult = NULL; ca_event->ErrCode = UPNP_E_SUCCESS; return ca_event->ErrCode; } // This function should set the state variable ConnectionStatus to // connecting, and then return synchronously, firing off a thread // asynchronously to actually change the status to connected. However, here we // assume that the external WAN device is configured and connected // outside of linux igd. int RequestConnection(struct Upnp_Action_Request *ca_event) { IXML_Document *propSet = NULL; //Immediatley Set connectionstatus to connected, and lastconnectionerror to none. strcpy(ConnectionStatus,"Connected"); strcpy(LastConnectionError, "ERROR_NONE"); trace(2, "RequestConnection recieved ... Setting Status to %s.", ConnectionStatus); // Build DOM Document with state variable connectionstatus and event it UpnpAddToPropertySet(&propSet, "ConnectionStatus", ConnectionStatus); // Send off notifications of state change UpnpNotifyExt(deviceHandle, ca_event->DevUDN, ca_event->ServiceID, propSet); ca_event->ErrCode = UPNP_E_SUCCESS; return ca_event->ErrCode; } int GetCommonLinkProperties(struct Upnp_Action_Request *ca_event) { char resultStr[RESULT_LEN]; IXML_Document *result; ca_event->ErrCode = UPNP_E_SUCCESS; snprintf(resultStr, RESULT_LEN, "\n" "Cable\n" "%s\n" "%s\n" "Up\n" "",g_vars.upstreamBitrate,g_vars.downstreamBitrate); // Create a IXML_Document from resultStr and return with ca_event if ((result = ixmlParseBuffer(resultStr)) != NULL) { ca_event->ActionResult = result; ca_event->ErrCode = UPNP_E_SUCCESS; } else { trace(1, "Error parsing Response to GetCommonLinkProperties: %s", resultStr); ca_event->ActionResult = NULL; ca_event->ErrCode = 402; } return(ca_event->ErrCode); } /* get specified statistic from /proc/net/dev */ int GetTotal(struct Upnp_Action_Request *ca_event, stats_t stat) { char dev[IFNAMSIZ], resultStr[RESULT_LEN]; const char *methods[STATS_LIMIT] = { "BytesSent", "BytesReceived", "PacketsSent", "PacketsReceived" }; unsigned long stats[STATS_LIMIT]; FILE *proc; IXML_Document *result; int read; proc = fopen("/proc/net/dev", "r"); if (!proc) { fprintf(stderr, "failed to open\n"); return 0; } /* skip first two lines */ fscanf(proc, "%*[^\n]\n%*[^\n]\n"); /* parse stats */ do read = fscanf(proc, "%[^:]:%lu %lu %*u %*u %*u %*u %*u %*u %lu %lu %*u %*u %*u %*u %*u %*u\n", dev, &stats[STATS_RX_BYTES], &stats[STATS_RX_PACKETS], &stats[STATS_TX_BYTES], &stats[STATS_TX_PACKETS]); while (read != EOF && (read == 5 && strncmp(dev, g_vars.extInterfaceName, IFNAMSIZ) != 0)); fclose(proc); snprintf(resultStr, RESULT_LEN, "\n" "%lu\n" "", methods[stat], methods[stat], stats[stat], methods[stat], methods[stat]); // Create a IXML_Document from resultStr and return with ca_event if ((result = ixmlParseBuffer(resultStr)) != NULL) { ca_event->ActionResult = result; ca_event->ErrCode = UPNP_E_SUCCESS; } else { trace(1, "Error parsing response to GetTotal: %s", resultStr); ca_event->ActionResult = NULL; ca_event->ErrCode = 402; } return (ca_event->ErrCode); } // Returns connection status related information to the control points int GetStatusInfo(struct Upnp_Action_Request *ca_event) { long int uptime; char resultStr[RESULT_LEN]; IXML_Document *result = NULL; uptime = (time(NULL) - startup_time); snprintf(resultStr, RESULT_LEN, "\n" "Connected\n" "ERROR_NONE\n" "%li\n" "", uptime); // Create a IXML_Document from resultStr and return with ca_event if ((result = ixmlParseBuffer(resultStr)) != NULL) { ca_event->ActionResult = result; ca_event->ErrCode = UPNP_E_SUCCESS; } else { trace(1, "Error parsing Response to GetStatusInfo: %s", resultStr); ca_event->ActionResult = NULL; ca_event->ErrCode = 402; } return(ca_event->ErrCode); } // Add New Port Map to the IGD int AddPortMapping(struct Upnp_Action_Request *ca_event) { char *remote_host=NULL; char *ext_port=NULL; char *proto=NULL; char *int_port=NULL; char *int_ip=NULL; char *int_duration=NULL; char *bool_enabled=NULL; char *desc=NULL; struct portMap *ret, *new; int result; char num[5]; // Maximum number of port mapping entries 9999 IXML_Document *propSet = NULL; int action_succeeded = 0; char resultStr[RESULT_LEN]; if ( (ext_port = GetFirstDocumentItem(ca_event->ActionRequest, "NewExternalPort") ) && (proto = GetFirstDocumentItem(ca_event->ActionRequest, "NewProtocol") ) && (int_port = GetFirstDocumentItem(ca_event->ActionRequest, "NewInternalPort") ) && (int_ip = GetFirstDocumentItem(ca_event->ActionRequest, "NewInternalClient") ) && (int_duration = GetFirstDocumentItem(ca_event->ActionRequest, "NewLeaseDuration") ) && (bool_enabled = GetFirstDocumentItem(ca_event->ActionRequest, "NewEnabled") ) && (desc = GetFirstDocumentItem(ca_event->ActionRequest, "NewPortMappingDescription") )) { remote_host = GetFirstDocumentItem(ca_event->ActionRequest, "NewRemoteHost"); // If port map with the same External Port, Protocol, and Internal Client exists // then, as per spec, we overwrite it (for simplicity, we delete and re-add at end of list) // Note: This may cause problems with GetGernericPortMappingEntry if a CP expects the overwritten // to be in the same place. if ((ret = pmlist_Find(ext_port, proto, int_ip)) != NULL) { trace(3, "Found port map to already exist. Replacing"); pmlist_Delete(ret); } new = pmlist_NewNode(atoi(bool_enabled), atol(int_duration), "", ext_port, int_port, proto, int_ip, desc); result = pmlist_PushBack(new); if (result==1) { ScheduleMappingExpiration(new,ca_event->DevUDN,ca_event->ServiceID); sprintf(num, "%d", pmlist_Size()); trace(3, "PortMappingNumberOfEntries: %d", pmlist_Size()); UpnpAddToPropertySet(&propSet, "PortMappingNumberOfEntries", num); UpnpNotifyExt(deviceHandle, ca_event->DevUDN, ca_event->ServiceID, propSet); ixmlDocument_free(propSet); trace(2, "AddPortMap: DevUDN: %s ServiceID: %s RemoteHost: %s Prot: %s ExtPort: %s Int: %s.%s", ca_event->DevUDN,ca_event->ServiceID,remote_host, proto, ext_port, int_ip, int_port); action_succeeded = 1; } else { if (result==718) { trace(1,"Failure in GateDeviceAddPortMapping: RemoteHost: %s Prot:%s ExtPort: %s Int: %s.%s\n", remote_host, proto, ext_port, int_ip, int_port); ca_event->ErrCode = 718; strcpy(ca_event->ErrStr, "ConflictInMappingEntry"); ca_event->ActionResult = NULL; } } } else { trace(1, "Failiure in GateDeviceAddPortMapping: Invalid Arguments!"); trace(1, " ExtPort: %s Proto: %s IntPort: %s IntIP: %s Dur: %s Ena: %s Desc: %s", ext_port, proto, int_port, int_ip, int_duration, bool_enabled, desc); ca_event->ErrCode = 402; strcpy(ca_event->ErrStr, "Invalid Args"); ca_event->ActionResult = NULL; } if (action_succeeded) { ca_event->ErrCode = UPNP_E_SUCCESS; snprintf(resultStr, RESULT_LEN, "\n%s\n", ca_event->ActionName, "urn:schemas-upnp-org:service:WANIPConnection:1", "", ca_event->ActionName); ca_event->ActionResult = ixmlParseBuffer(resultStr); } if (ext_port) free(ext_port); if (int_port) free(int_port); if (proto) free(proto); if (int_ip) free(int_ip); if (bool_enabled) free(bool_enabled); if (desc) free(desc); if (remote_host) free(remote_host); return(ca_event->ErrCode); } int GetGenericPortMappingEntry(struct Upnp_Action_Request *ca_event) { char *mapindex = NULL; struct portMap *temp; char result_param[RESULT_LEN]; char resultStr[RESULT_LEN]; int action_succeeded = 0; if ((mapindex = GetFirstDocumentItem(ca_event->ActionRequest, "NewPortMappingIndex"))) { temp = pmlist_FindByIndex(atoi(mapindex)); if (temp) { snprintf(result_param, RESULT_LEN, "%s%s%s%s%s%d%s%li", temp->m_RemoteHost, temp->m_ExternalPort, temp->m_PortMappingProtocol, temp->m_InternalPort, temp->m_InternalClient, temp->m_PortMappingEnabled, temp->m_PortMappingDescription, temp->m_PortMappingLeaseDuration); action_succeeded = 1; } if (action_succeeded) { ca_event->ErrCode = UPNP_E_SUCCESS; snprintf(resultStr, RESULT_LEN, "\n%s\n", ca_event->ActionName, "urn:schemas-upnp-org:service:WANIPConnection:1",result_param, ca_event->ActionName); ca_event->ActionResult = ixmlParseBuffer(resultStr); } else { ca_event->ErrCode = 713; strcpy(ca_event->ErrStr, "SpecifiedArrayIndexInvalid"); ca_event->ActionResult = NULL; } } else { trace(1, "Failure in GateDeviceGetGenericPortMappingEntry: Invalid Args"); ca_event->ErrCode = 402; strcpy(ca_event->ErrStr, "Invalid Args"); ca_event->ActionResult = NULL; } if (mapindex) free (mapindex); return (ca_event->ErrCode); } int GetSpecificPortMappingEntry(struct Upnp_Action_Request *ca_event) { char *ext_port=NULL; char *proto=NULL; char result_param[RESULT_LEN]; char resultStr[RESULT_LEN]; int action_succeeded = 0; struct portMap *temp; if ((ext_port = GetFirstDocumentItem(ca_event->ActionRequest, "NewExternalPort")) && (proto = GetFirstDocumentItem(ca_event->ActionRequest,"NewProtocol"))) { if ((strcmp(proto, "TCP") == 0) || (strcmp(proto, "UDP") == 0)) { temp = pmlist_FindSpecific (ext_port, proto); if (temp) { snprintf(result_param, RESULT_LEN, "%s%s%d%s%li", temp->m_InternalPort, temp->m_InternalClient, temp->m_PortMappingEnabled, temp->m_PortMappingDescription, temp->m_PortMappingLeaseDuration); action_succeeded = 1; } if (action_succeeded) { ca_event->ErrCode = UPNP_E_SUCCESS; snprintf(resultStr, RESULT_LEN, "\n%s\n", ca_event->ActionName, "urn:schemas-upnp-org:service:WANIPConnection:1",result_param, ca_event->ActionName); ca_event->ActionResult = ixmlParseBuffer(resultStr); } else { trace(2, "GateDeviceGetSpecificPortMappingEntry: PortMapping Doesn't Exist..."); ca_event->ErrCode = 714; strcpy(ca_event->ErrStr, "NoSuchEntryInArray"); ca_event->ActionResult = NULL; } } else { trace(1, "Failure in GateDeviceGetSpecificPortMappingEntry: Invalid NewProtocol=%s\n",proto); ca_event->ErrCode = 402; strcpy(ca_event->ErrStr, "Invalid Args"); ca_event->ActionResult = NULL; } } else { trace(1, "Failure in GateDeviceGetSpecificPortMappingEntry: Invalid Args"); ca_event->ErrCode = 402; strcpy(ca_event->ErrStr, "Invalid Args"); ca_event->ActionResult = NULL; } return (ca_event->ErrCode); } int GetExternalIPAddress(struct Upnp_Action_Request *ca_event) { char resultStr[RESULT_LEN]; IXML_Document *result = NULL; ca_event->ErrCode = UPNP_E_SUCCESS; GetIpAddressStr(ExternalIPAddress, g_vars.extInterfaceName); snprintf(resultStr, RESULT_LEN, "\n" "%s\n" "", ExternalIPAddress); // Create a IXML_Document from resultStr and return with ca_event if ((result = ixmlParseBuffer(resultStr)) != NULL) { ca_event->ActionResult = result; ca_event->ErrCode = UPNP_E_SUCCESS; } else { trace(1, "Error parsing Response to ExternalIPAddress: %s", resultStr); ca_event->ActionResult = NULL; ca_event->ErrCode = 402; } return(ca_event->ErrCode); } int DeletePortMapping(struct Upnp_Action_Request *ca_event) { char *ext_port=NULL; char *proto=NULL; int result=0; char num[5]; char resultStr[RESULT_LEN]; IXML_Document *propSet= NULL; int action_succeeded = 0; struct portMap *temp; if (((ext_port = GetFirstDocumentItem(ca_event->ActionRequest, "NewExternalPort")) && (proto = GetFirstDocumentItem(ca_event->ActionRequest, "NewProtocol")))) { if ((strcmp(proto, "TCP") == 0) || (strcmp(proto, "UDP") == 0)) { if ((temp = pmlist_FindSpecific(ext_port, proto))) result = pmlist_Delete(temp); if (result==1) { trace(2, "DeletePortMap: Proto:%s Port:%s\n",proto, ext_port); sprintf(num,"%d",pmlist_Size()); UpnpAddToPropertySet(&propSet,"PortMappingNumberOfEntries", num); UpnpNotifyExt(deviceHandle, ca_event->DevUDN,ca_event->ServiceID,propSet); ixmlDocument_free(propSet); action_succeeded = 1; } else { trace(1, "Failure in GateDeviceDeletePortMapping: DeletePortMap: Proto:%s Port:%s\n",proto, ext_port); ca_event->ErrCode = 714; strcpy(ca_event->ErrStr, "NoSuchEntryInArray"); ca_event->ActionResult = NULL; } } else { trace(1, "Failure in GateDeviceDeletePortMapping: Invalid NewProtocol=%s\n",proto); ca_event->ErrCode = 402; strcpy(ca_event->ErrStr, "Invalid Args"); ca_event->ActionResult = NULL; } } else { trace(1, "Failiure in GateDeviceDeletePortMapping: Invalid Arguments!"); ca_event->ErrCode = 402; strcpy(ca_event->ErrStr, "Invalid Args"); ca_event->ActionResult = NULL; } if (action_succeeded) { ca_event->ErrCode = UPNP_E_SUCCESS; snprintf(resultStr, RESULT_LEN, "\n%s\n", ca_event->ActionName, "urn:schemas-upnp-org:service:WANIPConnection:1", "", ca_event->ActionName); ca_event->ActionResult = ixmlParseBuffer(resultStr); } if (ext_port) free(ext_port); if (proto) free(proto); return(ca_event->ErrCode); } // From sampleutil.c included with libupnp char* GetFirstDocumentItem( IN IXML_Document * doc, IN const char *item ) { IXML_NodeList *nodeList = NULL; IXML_Node *textNode = NULL; IXML_Node *tmpNode = NULL; char *ret = NULL; nodeList = ixmlDocument_getElementsByTagName( doc, ( char * )item ); if( nodeList ) { if( ( tmpNode = ixmlNodeList_item( nodeList, 0 ) ) ) { textNode = ixmlNode_getFirstChild( tmpNode ); if (textNode != NULL) { ret = strdup( ixmlNode_getNodeValue( textNode ) ); } } } if( nodeList ) ixmlNodeList_free( nodeList ); return ret; } int ExpirationTimerThreadInit(void) { int retVal; ThreadPoolAttr attr; TPAttrInit( &attr ); TPAttrSetMaxThreads( &attr, MAX_THREADS ); TPAttrSetMinThreads( &attr, MIN_THREADS ); TPAttrSetJobsPerThread( &attr, JOBS_PER_THREAD ); TPAttrSetIdleTime( &attr, THREAD_IDLE_TIME ); if( ThreadPoolInit( &gExpirationThreadPool, &attr ) != UPNP_E_SUCCESS ) { return UPNP_E_INIT_FAILED; } if( ( retVal = TimerThreadInit( &gExpirationTimerThread, &gExpirationThreadPool ) ) != UPNP_E_SUCCESS ) { return retVal; } return 0; } int ExpirationTimerThreadShutdown(void) { return TimerThreadShutdown(&gExpirationTimerThread); } void free_expiration_event(expiration_event *event) { if (event->mapping!=NULL) event->mapping->expirationEventId = -1; free(event); } void ExpireMapping(void *input) { char num[5]; // Maximum number of port mapping entries 9999 IXML_Document *propSet = NULL; expiration_event *event = ( expiration_event * ) input; ithread_mutex_lock(&DevMutex); trace(2, "ExpireMapping: Proto:%s Port:%s\n", event->mapping->m_PortMappingProtocol, event->mapping->m_ExternalPort); //reset the event id before deleting the mapping so that pmlist_Delete //will not call CancelMappingExpiration event->mapping->expirationEventId = -1; pmlist_Delete(event->mapping); sprintf(num, "%d", pmlist_Size()); UpnpAddToPropertySet(&propSet, "PortMappingNumberOfEntries", num); UpnpNotifyExt(deviceHandle, event->DevUDN, event->ServiceID, propSet); ixmlDocument_free(propSet); trace(3, "ExpireMapping: UpnpNotifyExt(deviceHandle,%s,%s,propSet)\n PortMappingNumberOfEntries: %s", event->DevUDN, event->ServiceID, num); free_expiration_event(event); ithread_mutex_unlock(&DevMutex); } int ScheduleMappingExpiration(struct portMap *mapping, char *DevUDN, char *ServiceID) { int retVal = 0; ThreadPoolJob job; expiration_event *event; time_t curtime = time(NULL); if (mapping->m_PortMappingLeaseDuration > 0) { mapping->expirationTime = curtime + mapping->m_PortMappingLeaseDuration; } else { //client did not provide a duration, so use the default duration if (g_vars.duration==0) { return 1; //no default duration set } else if (g_vars.duration>0) { //relative duration mapping->expirationTime = curtime+g_vars.duration; } else { //g_vars.duration < 0 //absolute daily expiration time long int expclock = -1*g_vars.duration; struct tm *loctime = localtime(&curtime); long int curclock = loctime->tm_hour*3600 + loctime->tm_min*60 + loctime->tm_sec; long int diff = expclock-curclock; if (diff<60) //if exptime is in less than a minute (or in the past), schedule it in 24 hours instead diff += 24*60*60; mapping->expirationTime = curtime+diff; } } event = ( expiration_event * ) malloc( sizeof( expiration_event ) ); if( event == NULL ) { return 0; } event->mapping = mapping; if (strlen(DevUDN) < sizeof(event->DevUDN)) strcpy(event->DevUDN, DevUDN); else strcpy(event->DevUDN, ""); if (strlen(ServiceID) < sizeof(event->ServiceID)) strcpy(event->ServiceID, ServiceID); else strcpy(event->ServiceID, ""); TPJobInit( &job, ( start_routine ) ExpireMapping, event ); TPJobSetFreeFunction( &job, ( free_routine ) free_expiration_event ); if( ( retVal = TimerThreadSchedule( &gExpirationTimerThread, mapping->expirationTime, ABS_SEC, &job, SHORT_TERM, &( event->eventId ) ) ) != UPNP_E_SUCCESS ) { free( event ); mapping->expirationEventId = -1; return 0; } mapping->expirationEventId = event->eventId; trace(3,"ScheduleMappingExpiration: DevUDN: %s ServiceID: %s Proto: %s ExtPort: %s Int: %s.%s at: %s eventId: %d",event->DevUDN,event->ServiceID,mapping->m_PortMappingProtocol, mapping->m_ExternalPort, mapping->m_InternalClient, mapping->m_InternalPort, ctime(&(mapping->expirationTime)), event->eventId); return event->eventId; } int CancelMappingExpiration(int expirationEventId) { ThreadPoolJob job; if (expirationEventId<0) return 1; trace(3,"CancelMappingExpiration: eventId: %d",expirationEventId); if (TimerThreadRemove(&gExpirationTimerThread,expirationEventId,&job)==0) { free_expiration_event((expiration_event *)job.arg); } else { trace(1," TimerThreadRemove failed!"); } return 1; } void DeleteAllPortMappings(void) { IXML_Document *propSet = NULL; ithread_mutex_lock(&DevMutex); pmlist_FreeList(); UpnpAddToPropertySet(&propSet, "PortMappingNumberOfEntries", "0"); UpnpNotifyExt(deviceHandle, gateUDN, "urn:upnp-org:serviceId:WANIPConn1", propSet); ixmlDocument_free(propSet); trace(2, "DeleteAllPortMappings: UpnpNotifyExt(deviceHandle,%s,%s,propSet)\n PortMappingNumberOfEntries: %s", gateUDN, "urn:upnp-org:serviceId:WANIPConn1", "0"); ithread_mutex_unlock(&DevMutex); }