📄 gatedevice.c
字号:
#include <syslog.h>#include <stdlib.h>#include <upnp/ixml.h>#include <string.h>#include <time.h>#include <upnp/upnp.h>#include <upnp/upnptools.h>#include <upnp/TimerThread.h>#include "globals.h"#include "gatedevice.h"#include "pmlist.h"#include "util.h"//Definitions for mapping expiration timer threadstatic TimerThread gExpirationTimerThread;static ThreadPool gExpirationThreadPool;// MUTEX for locking shared state variables whenver they are changedstatic 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 deprecatedint 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 initializationsint 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 notificationsint 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 Requestsint 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 typeint GetConnectionTypeInfo (struct Upnp_Action_Request *ca_event){ char resultStr[RESULT_LEN]; IXML_Document *result; snprintf(resultStr, RESULT_LEN, "<u:GetConnectionTypeInfoResponse xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">\n" "<NewConnectionType>IP_Routed</NewConnectionType>\n" "<NewPossibleConnectionTypes>IP_Routed</NewPossibleConnectionTypes>" "</u:GetConnectionTypeInfoResponse>"); // 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 = 1int GetNATRSIPStatus(struct Upnp_Action_Request *ca_event){ char resultStr[RESULT_LEN]; IXML_Document *result; snprintf(resultStr, RESULT_LEN, "<u:GetNATRSIPStatusResponse xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">\n" "<NewRSIPAvailable>0</NewRSIPAvailable>\n" "<NewNATEnabled>1</NewNATEnabled>\n" "</u:GetNATRSIPStatusResponse>"); // 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, "<u:GetCommonLinkPropertiesResponse xmlns:u=\"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1\">\n" "<NewWANAccessType>Cable</NewWANAccessType>\n" "<NewLayer1UpstreamMaxBitRate>%s</NewLayer1UpstreamMaxBitRate>\n" "<NewLayer1DownstreamMaxBitRate>%s</NewLayer1DownstreamMaxBitRate>\n" "<NewPhysicalLinkStatus>Up</NewPhysicalLinkStatus>\n" "</u:GetCommonLinkPropertiesResponse>",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, "<u:GetTotal%sResponse xmlns:u=\"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1\">\n" "<NewTotal%s>%lu</NewTotal%s>\n" "</u:GetTotal%sResponse>", 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 pointsint 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, "<u:GetStatusInfoResponse xmlns:u=\"urn:schemas-upnp-org:service:GetStatusInfo:1\">\n" "<NewConnectionStatus>Connected</NewConnectionStatus>\n" "<NewLastConnectionError>ERROR_NONE</NewLastConnectionError>\n" "<NewUptime>%li</NewUptime>\n" "</u:GetStatusInfoResponse>", 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 IGDint 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.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -