Original linux-igd sources from CVS repo:
[igd2-for-linux:wanipconnection2.git] / linuxigd2 / gatedevice.c
1 #include <syslog.h>
2 #include <stdlib.h>
3 #include <upnp/ixml.h>
4 #include <string.h>
5 #include <time.h>
6 #include <upnp/upnp.h>
7 #include <upnp/upnptools.h>
8 #include <upnp/TimerThread.h>
9 #include "globals.h"
10 #include "gatedevice.h"
11 #include "pmlist.h"
12 #include "util.h"
13
14 //Definitions for mapping expiration timer thread
15 static TimerThread gExpirationTimerThread;
16 static ThreadPool gExpirationThreadPool;
17
18 // MUTEX for locking shared state variables whenver they are changed
19 static ithread_mutex_t DevMutex = PTHREAD_MUTEX_INITIALIZER;
20
21 // Main event handler for callbacks from the SDK.  Determine type of event
22 // and dispatch to the appropriate handler (Note: Get Var Request deprecated
23 int EventHandler(Upnp_EventType EventType, void *Event, void *Cookie)
24 {
25         switch (EventType)
26         {
27                 case UPNP_EVENT_SUBSCRIPTION_REQUEST:
28                         HandleSubscriptionRequest((struct Upnp_Subscription_Request *) Event);
29                         break;
30                 // -- Deprecated --
31                 case UPNP_CONTROL_GET_VAR_REQUEST:
32                         HandleGetVarRequest((struct Upnp_State_Var_Request *) Event);
33                         break;
34                 case UPNP_CONTROL_ACTION_REQUEST:
35                         HandleActionRequest((struct Upnp_Action_Request *) Event);
36                         break;
37                 default:
38                         trace(1, "Error in EventHandler: Unknown event type %d",
39                                                 EventType);
40         }
41         return (0);
42 }
43
44 // Grab our UDN from the Description Document.  This may not be needed, 
45 // the UDN comes with the request, but we leave this for other device initializations
46 int StateTableInit(char *descDocUrl)
47 {
48         IXML_Document *ixmlDescDoc;
49         int ret;
50
51         if ((ret = UpnpDownloadXmlDoc(descDocUrl, &ixmlDescDoc)) != UPNP_E_SUCCESS)
52         {
53                 syslog(LOG_ERR, "Could not parse description document. Exiting ...");
54                 UpnpFinish();
55                 exit(0);
56         }
57
58         // Get the UDN from the description document, then free the DescDoc's memory
59         gateUDN = GetFirstDocumentItem(ixmlDescDoc, "UDN");
60         ixmlDocument_free(ixmlDescDoc);
61                 
62         // Initialize our linked list of port mappings.
63         pmlist_Head = pmlist_Current = NULL;
64         PortMappingNumberOfEntries = 0;
65
66         return (ret);
67 }
68
69 // Handles subscription request for state variable notifications
70 int HandleSubscriptionRequest(struct Upnp_Subscription_Request *sr_event)
71 {
72         IXML_Document *propSet = NULL;
73         
74         ithread_mutex_lock(&DevMutex);
75
76         if (strcmp(sr_event->UDN, gateUDN) == 0)
77         {
78                 // WAN Common Interface Config Device Notifications
79                 if (strcmp(sr_event->ServiceId, "urn:upnp-org:serviceId:WANCommonIFC1") == 0)
80                 {
81                         trace(3, "Recieved request to subscribe to WANCommonIFC1");
82                         UpnpAddToPropertySet(&propSet, "PhysicalLinkStatus", "Up");
83                         UpnpAcceptSubscriptionExt(deviceHandle, sr_event->UDN, sr_event->ServiceId,
84                                                   propSet, sr_event->Sid);
85                         ixmlDocument_free(propSet);
86                 }
87                 // WAN IP Connection Device Notifications
88                 else if (strcmp(sr_event->ServiceId, "urn:upnp-org:serviceId:WANIPConn1") == 0)
89                 {
90                         GetIpAddressStr(ExternalIPAddress, g_vars.extInterfaceName);
91                         trace(3, "Received request to subscribe to WANIPConn1");
92                         UpnpAddToPropertySet(&propSet, "PossibleConnectionTypes","IP_Routed");
93                         UpnpAddToPropertySet(&propSet, "ConnectionStatus","Connected");
94                         UpnpAddToPropertySet(&propSet, "ExternalIPAddress", ExternalIPAddress);
95                         UpnpAddToPropertySet(&propSet, "PortMappingNumberOfEntries","0");
96                         UpnpAcceptSubscriptionExt(deviceHandle, sr_event->UDN, sr_event->ServiceId,
97                                                   propSet, sr_event->Sid);
98                         ixmlDocument_free(propSet);
99                 }
100         }
101         ithread_mutex_unlock(&DevMutex);
102         return(1);
103 }
104
105 int HandleGetVarRequest(struct Upnp_State_Var_Request *gv_request)
106 {
107         // GET VAR REQUEST DEPRECATED FROM UPnP SPECIFICATIONS 
108         // Report this in debug and ignore requests.  If anyone experiences problems
109         // please let us know.
110         trace(3, "Deprecated Get Variable Request received. Ignoring.");
111         return 1;
112 }
113
114 int HandleActionRequest(struct Upnp_Action_Request *ca_event)
115 {
116         int result = 0;
117
118         ithread_mutex_lock(&DevMutex);
119         
120         if (strcmp(ca_event->DevUDN, gateUDN) == 0)
121         {
122                 // Common debugging info, hopefully gets removed soon.
123                 trace(3, "ActionName = %s", ca_event->ActionName);
124                 
125                 if (strcmp(ca_event->ServiceID, "urn:upnp-org:serviceId:WANIPConn1") == 0)
126                 {
127                         if (strcmp(ca_event->ActionName,"GetConnectionTypeInfo") == 0)
128                           result = GetConnectionTypeInfo(ca_event);
129                         else if (strcmp(ca_event->ActionName,"GetNATRSIPStatus") == 0)
130                           result = GetNATRSIPStatus(ca_event);
131                         else if (strcmp(ca_event->ActionName,"SetConnectionType") == 0)
132                           result = SetConnectionType(ca_event);
133                         else if (strcmp(ca_event->ActionName,"RequestConnection") == 0)
134                           result = RequestConnection(ca_event);
135                         else if (strcmp(ca_event->ActionName,"AddPortMapping") == 0)
136                           result = AddPortMapping(ca_event);
137                         else if (strcmp(ca_event->ActionName,"GetGenericPortMappingEntry") == 0)
138                           result = GetGenericPortMappingEntry(ca_event);
139                         else if (strcmp(ca_event->ActionName,"GetSpecificPortMappingEntry") == 0)
140                           result = GetSpecificPortMappingEntry(ca_event);
141                         else if (strcmp(ca_event->ActionName,"GetExternalIPAddress") == 0)
142                           result = GetExternalIPAddress(ca_event);
143                         else if (strcmp(ca_event->ActionName,"DeletePortMapping") == 0)
144                           result = DeletePortMapping(ca_event);
145                         else if (strcmp(ca_event->ActionName,"GetStatusInfo") == 0)
146                           result = GetStatusInfo(ca_event);
147         
148                         // Intentionally Non-Implemented Functions -- To be added later
149                         /*else if (strcmp(ca_event->ActionName,"RequestTermination") == 0)
150                                 result = RequestTermination(ca_event);
151                         else if (strcmp(ca_event->ActionName,"ForceTermination") == 0)
152                                 result = ForceTermination(ca_event);
153                         else if (strcmp(ca_event->ActionName,"SetAutoDisconnectTime") == 0)
154                                 result = SetAutoDisconnectTime(ca_event);
155                         else if (strcmp(ca_event->ActionName,"SetIdleDisconnectTime") == 0)
156                                 result = SetIdleDisconnectTime(ca_event);
157                         else if (strcmp(ca_event->ActionName,"SetWarnDisconnectDelay") == 0)
158                                 result = SetWarnDisconnectDelay(ca_event);
159                         else if (strcmp(ca_event->ActionName,"GetAutoDisconnectTime") == 0)
160                                 result = GetAutoDisconnectTime(ca_event);
161                         else if (strcmp(ca_event->ActionName,"GetIdleDisconnectTime") == 0)
162                                 result = GetIdleDisconnectTime(ca_event);
163                         else if (strcmp(ca_event->ActionName,"GetWarnDisconnectDelay") == 0)
164                                 result = GetWarnDisconnectDelay(ca_event);*/
165                         else result = InvalidAction(ca_event);
166                 }
167                 else if (strcmp(ca_event->ServiceID,"urn:upnp-org:serviceId:WANCommonIFC1") == 0)
168                 {
169                         if (strcmp(ca_event->ActionName,"GetTotalBytesSent") == 0)
170                                 result = GetTotal(ca_event, STATS_TX_BYTES);
171                         else if (strcmp(ca_event->ActionName,"GetTotalBytesReceived") == 0)
172                                 result = GetTotal(ca_event, STATS_RX_BYTES);
173                         else if (strcmp(ca_event->ActionName,"GetTotalPacketsSent") == 0)
174                                 result = GetTotal(ca_event, STATS_TX_PACKETS);
175                         else if (strcmp(ca_event->ActionName,"GetTotalPacketsReceived") == 0)
176                                 result = GetTotal(ca_event, STATS_RX_PACKETS);
177                         else if (strcmp(ca_event->ActionName,"GetCommonLinkProperties") == 0)
178                                 result = GetCommonLinkProperties(ca_event);
179                         else 
180                         {
181                                 trace(1, "Invalid Action Request : %s",ca_event->ActionName);
182                                 result = InvalidAction(ca_event);
183                         }
184                 } 
185         }
186         
187         ithread_mutex_unlock(&DevMutex);
188
189         return (result);
190 }
191
192 // Default Action when we receive unknown Action Requests
193 int InvalidAction(struct Upnp_Action_Request *ca_event)
194 {
195         ca_event->ErrCode = 401;
196         strcpy(ca_event->ErrStr, "Invalid Action");
197         ca_event->ActionResult = NULL;
198         return (ca_event->ErrCode);
199 }
200
201 // As IP_Routed is the only relevant Connection Type for Linux-IGD
202 // we respond with IP_Routed as both current type and only type
203 int GetConnectionTypeInfo (struct Upnp_Action_Request *ca_event)
204 {
205         char resultStr[RESULT_LEN];
206         IXML_Document *result;
207
208         snprintf(resultStr, RESULT_LEN,
209                 "<u:GetConnectionTypeInfoResponse xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">\n"
210                 "<NewConnectionType>IP_Routed</NewConnectionType>\n"
211                 "<NewPossibleConnectionTypes>IP_Routed</NewPossibleConnectionTypes>"
212                 "</u:GetConnectionTypeInfoResponse>");
213
214    // Create a IXML_Document from resultStr and return with ca_event
215    if ((result = ixmlParseBuffer(resultStr)) != NULL)
216    {
217       ca_event->ActionResult = result;
218       ca_event->ErrCode = UPNP_E_SUCCESS;
219    }
220    else
221    {
222       trace(1, "Error parsing Response to GetConnectionTypeinfo: %s", resultStr);
223       ca_event->ActionResult = NULL;
224       ca_event->ErrCode = 402;
225    }
226
227         return(ca_event->ErrCode);
228 }
229
230 // Linux-IGD does not support RSIP.  However NAT is of course
231 // so respond with NewNATEnabled = 1
232 int GetNATRSIPStatus(struct Upnp_Action_Request *ca_event)
233 {
234    char resultStr[RESULT_LEN];
235         IXML_Document *result;
236
237    snprintf(resultStr, RESULT_LEN, "<u:GetNATRSIPStatusResponse xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">\n"
238                                                         "<NewRSIPAvailable>0</NewRSIPAvailable>\n"
239                                                                         "<NewNATEnabled>1</NewNATEnabled>\n"
240                                                                 "</u:GetNATRSIPStatusResponse>");
241
242         // Create a IXML_Document from resultStr and return with ca_event
243         if ((result = ixmlParseBuffer(resultStr)) != NULL)
244         {
245                 ca_event->ActionResult = result;
246                 ca_event->ErrCode = UPNP_E_SUCCESS;     
247         }
248    else
249         {
250                 trace(1, "Error parsing Response to GetNATRSIPStatus: %s", resultStr);
251                 ca_event->ActionResult = NULL;
252                 ca_event->ErrCode = 402;
253         }
254
255         return(ca_event->ErrCode);
256 }
257
258
259 // Connection Type is a Read Only Variable as linux-igd is only
260 // a device that supports a NATing IP router (not an Ethernet
261 // bridge).  Possible other uses may be explored.
262 int SetConnectionType(struct Upnp_Action_Request *ca_event)
263 {
264         // Ignore requests
265         ca_event->ActionResult = NULL;
266         ca_event->ErrCode = UPNP_E_SUCCESS;
267         return ca_event->ErrCode;
268 }
269
270 // This function should set the state variable ConnectionStatus to
271 // connecting, and then return synchronously, firing off a thread
272 // asynchronously to actually change the status to connected.  However, here we
273 // assume that the external WAN device is configured and connected
274 // outside of linux igd.
275 int RequestConnection(struct Upnp_Action_Request *ca_event)
276 {
277         
278         IXML_Document *propSet = NULL;
279         
280         //Immediatley Set connectionstatus to connected, and lastconnectionerror to none.
281         strcpy(ConnectionStatus,"Connected");
282         strcpy(LastConnectionError, "ERROR_NONE");
283         trace(2, "RequestConnection recieved ... Setting Status to %s.", ConnectionStatus);
284
285         // Build DOM Document with state variable connectionstatus and event it
286         UpnpAddToPropertySet(&propSet, "ConnectionStatus", ConnectionStatus);
287         
288         // Send off notifications of state change
289         UpnpNotifyExt(deviceHandle, ca_event->DevUDN, ca_event->ServiceID, propSet);
290
291         ca_event->ErrCode = UPNP_E_SUCCESS;
292         return ca_event->ErrCode;
293 }
294
295
296 int GetCommonLinkProperties(struct Upnp_Action_Request *ca_event)
297 {
298    char resultStr[RESULT_LEN];
299         IXML_Document *result;
300         
301         ca_event->ErrCode = UPNP_E_SUCCESS;
302         snprintf(resultStr, RESULT_LEN,
303                 "<u:GetCommonLinkPropertiesResponse xmlns:u=\"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1\">\n"
304                 "<NewWANAccessType>Cable</NewWANAccessType>\n"
305                 "<NewLayer1UpstreamMaxBitRate>%s</NewLayer1UpstreamMaxBitRate>\n"
306                 "<NewLayer1DownstreamMaxBitRate>%s</NewLayer1DownstreamMaxBitRate>\n"
307                 "<NewPhysicalLinkStatus>Up</NewPhysicalLinkStatus>\n"
308                 "</u:GetCommonLinkPropertiesResponse>",g_vars.upstreamBitrate,g_vars.downstreamBitrate);
309
310    // Create a IXML_Document from resultStr and return with ca_event
311    if ((result = ixmlParseBuffer(resultStr)) != NULL)
312    {
313       ca_event->ActionResult = result;
314       ca_event->ErrCode = UPNP_E_SUCCESS;
315    }
316    else
317    {
318       trace(1, "Error parsing Response to GetCommonLinkProperties: %s", resultStr);
319       ca_event->ActionResult = NULL;
320       ca_event->ErrCode = 402;
321    }
322
323         return(ca_event->ErrCode);
324 }
325
326 /* get specified statistic from /proc/net/dev */
327 int GetTotal(struct Upnp_Action_Request *ca_event, stats_t stat)
328 {
329         char dev[IFNAMSIZ], resultStr[RESULT_LEN];
330         const char *methods[STATS_LIMIT] =
331                 { "BytesSent", "BytesReceived", "PacketsSent", "PacketsReceived" };
332         unsigned long stats[STATS_LIMIT];
333         FILE *proc;
334         IXML_Document *result;
335         int read;
336         
337         proc = fopen("/proc/net/dev", "r");
338         if (!proc)
339         {
340                 fprintf(stderr, "failed to open\n");
341                 return 0;
342         }
343
344         /* skip first two lines */
345         fscanf(proc, "%*[^\n]\n%*[^\n]\n");
346
347         /* parse stats */
348         do
349                 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]);
350         while (read != EOF && (read == 5 && strncmp(dev, g_vars.extInterfaceName, IFNAMSIZ) != 0));
351
352         fclose(proc);
353
354         snprintf(resultStr, RESULT_LEN,
355                 "<u:GetTotal%sResponse xmlns:u=\"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1\">\n"
356                 "<NewTotal%s>%lu</NewTotal%s>\n"
357                 "</u:GetTotal%sResponse>", 
358                 methods[stat], methods[stat], stats[stat], methods[stat], methods[stat]);
359
360         // Create a IXML_Document from resultStr and return with ca_event
361         if ((result = ixmlParseBuffer(resultStr)) != NULL)
362         {
363                 ca_event->ActionResult = result;
364                 ca_event->ErrCode = UPNP_E_SUCCESS;
365         }
366         else
367         {
368                 trace(1, "Error parsing response to GetTotal: %s", resultStr);
369                 ca_event->ActionResult = NULL;
370                 ca_event->ErrCode = 402;
371         }
372
373         return (ca_event->ErrCode);
374 }
375
376 // Returns connection status related information to the control points
377 int GetStatusInfo(struct Upnp_Action_Request *ca_event)
378 {
379    long int uptime;
380    char resultStr[RESULT_LEN];
381         IXML_Document *result = NULL;
382
383    uptime = (time(NULL) - startup_time);
384    
385         snprintf(resultStr, RESULT_LEN,
386                 "<u:GetStatusInfoResponse xmlns:u=\"urn:schemas-upnp-org:service:GetStatusInfo:1\">\n"
387                 "<NewConnectionStatus>Connected</NewConnectionStatus>\n"
388                 "<NewLastConnectionError>ERROR_NONE</NewLastConnectionError>\n"
389                 "<NewUptime>%li</NewUptime>\n"
390                 "</u:GetStatusInfoResponse>", 
391                 uptime);
392    
393         // Create a IXML_Document from resultStr and return with ca_event
394    if ((result = ixmlParseBuffer(resultStr)) != NULL)
395    {
396       ca_event->ActionResult = result;
397       ca_event->ErrCode = UPNP_E_SUCCESS;
398    }
399    else
400    {
401      trace(1, "Error parsing Response to GetStatusInfo: %s", resultStr);
402       ca_event->ActionResult = NULL;
403       ca_event->ErrCode = 402;
404    }
405
406    return(ca_event->ErrCode);
407 }
408
409 // Add New Port Map to the IGD
410 int AddPortMapping(struct Upnp_Action_Request *ca_event)
411 {
412         char *remote_host=NULL;
413         char *ext_port=NULL;
414         char *proto=NULL;
415         char *int_port=NULL;
416         char *int_ip=NULL;
417         char *int_duration=NULL;
418         char *bool_enabled=NULL;
419         char *desc=NULL;
420         struct portMap *ret, *new;
421         int result;
422         char num[5]; // Maximum number of port mapping entries 9999
423         IXML_Document *propSet = NULL;
424         int action_succeeded = 0;
425         char resultStr[RESULT_LEN];
426
427         if ( (ext_port = GetFirstDocumentItem(ca_event->ActionRequest, "NewExternalPort") )
428              && (proto = GetFirstDocumentItem(ca_event->ActionRequest, "NewProtocol") )
429              && (int_port = GetFirstDocumentItem(ca_event->ActionRequest, "NewInternalPort") )
430              && (int_ip = GetFirstDocumentItem(ca_event->ActionRequest, "NewInternalClient") )
431              && (int_duration = GetFirstDocumentItem(ca_event->ActionRequest, "NewLeaseDuration") )
432              && (bool_enabled = GetFirstDocumentItem(ca_event->ActionRequest, "NewEnabled") )
433              && (desc = GetFirstDocumentItem(ca_event->ActionRequest, "NewPortMappingDescription") ))
434         {
435           remote_host = GetFirstDocumentItem(ca_event->ActionRequest, "NewRemoteHost");
436                 // If port map with the same External Port, Protocol, and Internal Client exists
437                 // then, as per spec, we overwrite it (for simplicity, we delete and re-add at end of list)
438                 // Note: This may cause problems with GetGernericPortMappingEntry if a CP expects the overwritten
439                 // to be in the same place.
440                 if ((ret = pmlist_Find(ext_port, proto, int_ip)) != NULL)
441                 {
442                                 trace(3, "Found port map to already exist.  Replacing");
443                                 pmlist_Delete(ret);
444                 }
445                         
446                 new = pmlist_NewNode(atoi(bool_enabled), atol(int_duration), "", ext_port, int_port, proto, int_ip, desc); 
447                 result = pmlist_PushBack(new);
448                 if (result==1)
449                 {
450                         ScheduleMappingExpiration(new,ca_event->DevUDN,ca_event->ServiceID);
451                         sprintf(num, "%d", pmlist_Size());
452                         trace(3, "PortMappingNumberOfEntries: %d", pmlist_Size());
453                         UpnpAddToPropertySet(&propSet, "PortMappingNumberOfEntries", num);                              
454                         UpnpNotifyExt(deviceHandle, ca_event->DevUDN, ca_event->ServiceID, propSet);
455                         ixmlDocument_free(propSet);
456                         trace(2, "AddPortMap: DevUDN: %s ServiceID: %s RemoteHost: %s Prot: %s ExtPort: %s Int: %s.%s",
457                                             ca_event->DevUDN,ca_event->ServiceID,remote_host, proto, ext_port, int_ip, int_port);
458                         action_succeeded = 1;
459                 }
460                 else
461                 {
462                         if (result==718)
463                         {
464                                 trace(1,"Failure in GateDeviceAddPortMapping: RemoteHost: %s Prot:%s ExtPort: %s Int: %s.%s\n",
465                                                     remote_host, proto, ext_port, int_ip, int_port);
466                                 ca_event->ErrCode = 718;
467                                 strcpy(ca_event->ErrStr, "ConflictInMappingEntry");
468                                 ca_event->ActionResult = NULL;
469                         }
470                 }
471         }
472         else
473         {
474           trace(1, "Failiure in GateDeviceAddPortMapping: Invalid Arguments!");
475           trace(1, "  ExtPort: %s Proto: %s IntPort: %s IntIP: %s Dur: %s Ena: %s Desc: %s",
476                 ext_port, proto, int_port, int_ip, int_duration, bool_enabled, desc);
477           ca_event->ErrCode = 402;
478           strcpy(ca_event->ErrStr, "Invalid Args");
479           ca_event->ActionResult = NULL;
480         }
481         
482         if (action_succeeded)
483         {
484                 ca_event->ErrCode = UPNP_E_SUCCESS;
485                 snprintf(resultStr, RESULT_LEN, "<u:%sResponse xmlns:u=\"%s\">\n%s\n</u:%sResponse>",
486                         ca_event->ActionName, "urn:schemas-upnp-org:service:WANIPConnection:1", "", ca_event->ActionName);
487                 ca_event->ActionResult = ixmlParseBuffer(resultStr);
488         }
489
490         if (ext_port) free(ext_port);
491         if (int_port) free(int_port);
492         if (proto) free(proto);
493         if (int_ip) free(int_ip);
494         if (bool_enabled) free(bool_enabled);
495         if (desc) free(desc);
496         if (remote_host) free(remote_host);
497
498         return(ca_event->ErrCode);
499 }
500
501 int GetGenericPortMappingEntry(struct Upnp_Action_Request *ca_event)
502 {
503         char *mapindex = NULL;
504         struct portMap *temp;
505         char result_param[RESULT_LEN];
506         char resultStr[RESULT_LEN];
507         int action_succeeded = 0;
508
509         if ((mapindex = GetFirstDocumentItem(ca_event->ActionRequest, "NewPortMappingIndex")))
510         {
511                 temp = pmlist_FindByIndex(atoi(mapindex));
512                 if (temp)
513                 {
514                         snprintf(result_param, RESULT_LEN, "<NewRemoteHost>%s</NewRemoteHost><NewExternalPort>%s</NewExternalPort><NewProtocol>%s</NewProtocol><NewInternalPort>%s</NewInternalPort><NewInternalClient>%s</NewInternalClient><NewEnabled>%d</NewEnabled><NewPortMappingDescription>%s</NewPortMappingDescription><NewLeaseDuration>%li</NewLeaseDuration>", temp->m_RemoteHost, temp->m_ExternalPort, temp->m_PortMappingProtocol, temp->m_InternalPort, temp->m_InternalClient, temp->m_PortMappingEnabled, temp->m_PortMappingDescription, temp->m_PortMappingLeaseDuration);
515                         action_succeeded = 1;
516                 }
517       if (action_succeeded)
518       {
519          ca_event->ErrCode = UPNP_E_SUCCESS;
520                    snprintf(resultStr, RESULT_LEN, "<u:%sResponse xmlns:u=\"%s\">\n%s\n</u:%sResponse>", ca_event->ActionName,
521                            "urn:schemas-upnp-org:service:WANIPConnection:1",result_param, ca_event->ActionName);
522                    ca_event->ActionResult = ixmlParseBuffer(resultStr);
523       }
524       else
525       {
526          ca_event->ErrCode = 713;
527                         strcpy(ca_event->ErrStr, "SpecifiedArrayIndexInvalid");
528                         ca_event->ActionResult = NULL;
529       }
530
531    }
532    else
533    {
534             trace(1, "Failure in GateDeviceGetGenericPortMappingEntry: Invalid Args");
535             ca_event->ErrCode = 402;
536                  strcpy(ca_event->ErrStr, "Invalid Args");
537                  ca_event->ActionResult = NULL;
538    }
539         if (mapindex) free (mapindex);
540         return (ca_event->ErrCode);
541         
542 }
543 int GetSpecificPortMappingEntry(struct Upnp_Action_Request *ca_event)
544 {
545    char *ext_port=NULL;
546    char *proto=NULL;
547    char result_param[RESULT_LEN];
548    char resultStr[RESULT_LEN];
549    int action_succeeded = 0;
550         struct portMap *temp;
551
552    if ((ext_port = GetFirstDocumentItem(ca_event->ActionRequest, "NewExternalPort"))
553       && (proto = GetFirstDocumentItem(ca_event->ActionRequest,"NewProtocol")))
554    {
555       if ((strcmp(proto, "TCP") == 0) || (strcmp(proto, "UDP") == 0))
556       {
557                         temp = pmlist_FindSpecific (ext_port, proto);
558                         if (temp)
559                         {
560                                 snprintf(result_param, RESULT_LEN, "<NewInternalPort>%s</NewInternalPort><NewInternalClient>%s</NewInternalClient><NewEnabled>%d</NewEnabled><NewPortMappingDescription>%s</NewPortMappingDescription><NewLeaseDuration>%li</NewLeaseDuration>",
561             temp->m_InternalPort,
562             temp->m_InternalClient,
563             temp->m_PortMappingEnabled,
564                                 temp->m_PortMappingDescription,
565             temp->m_PortMappingLeaseDuration);
566             action_succeeded = 1;
567                         }
568          if (action_succeeded)
569          {
570             ca_event->ErrCode = UPNP_E_SUCCESS;
571             snprintf(resultStr, RESULT_LEN, "<u:%sResponse xmlns:u=\"%s\">\n%s\n</u:%sResponse>", ca_event->ActionName,
572                     "urn:schemas-upnp-org:service:WANIPConnection:1",result_param, ca_event->ActionName);
573             ca_event->ActionResult = ixmlParseBuffer(resultStr);
574          }
575          else
576          {
577             trace(2, "GateDeviceGetSpecificPortMappingEntry: PortMapping Doesn't Exist...");
578             ca_event->ErrCode = 714;
579             strcpy(ca_event->ErrStr, "NoSuchEntryInArray");
580             ca_event->ActionResult = NULL;
581          }
582       }
583       else
584       {
585               trace(1, "Failure in GateDeviceGetSpecificPortMappingEntry: Invalid NewProtocol=%s\n",proto);
586               ca_event->ErrCode = 402;
587               strcpy(ca_event->ErrStr, "Invalid Args");
588               ca_event->ActionResult = NULL;
589       }
590    }
591    else
592    {
593       trace(1, "Failure in GateDeviceGetSpecificPortMappingEntry: Invalid Args");
594       ca_event->ErrCode = 402;
595       strcpy(ca_event->ErrStr, "Invalid Args");
596       ca_event->ActionResult = NULL;
597    }
598
599    return (ca_event->ErrCode);
600
601
602 }
603 int GetExternalIPAddress(struct Upnp_Action_Request *ca_event)
604 {
605    char resultStr[RESULT_LEN];
606         IXML_Document *result = NULL;
607
608    ca_event->ErrCode = UPNP_E_SUCCESS;
609    GetIpAddressStr(ExternalIPAddress, g_vars.extInterfaceName);
610    snprintf(resultStr, RESULT_LEN, "<u:GetExternalIPAddressResponse xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">\n"
611                                                                                 "<NewExternalIPAddress>%s</NewExternalIPAddress>\n"
612                                                                 "</u:GetExternalIPAddressResponse>", ExternalIPAddress);
613
614    // Create a IXML_Document from resultStr and return with ca_event
615    if ((result = ixmlParseBuffer(resultStr)) != NULL)
616    {
617       ca_event->ActionResult = result;
618       ca_event->ErrCode = UPNP_E_SUCCESS;
619    }
620    else
621    {
622       trace(1, "Error parsing Response to ExternalIPAddress: %s", resultStr);
623       ca_event->ActionResult = NULL;
624       ca_event->ErrCode = 402;
625    }
626
627    return(ca_event->ErrCode);
628 }
629
630 int DeletePortMapping(struct Upnp_Action_Request *ca_event)
631 {
632    char *ext_port=NULL;
633    char *proto=NULL;
634    int result=0;
635    char num[5];
636    char resultStr[RESULT_LEN];
637    IXML_Document *propSet= NULL;
638    int action_succeeded = 0;
639         struct portMap *temp;
640
641    if (((ext_port = GetFirstDocumentItem(ca_event->ActionRequest, "NewExternalPort")) &&
642       (proto = GetFirstDocumentItem(ca_event->ActionRequest, "NewProtocol"))))
643    {
644
645      if ((strcmp(proto, "TCP") == 0) || (strcmp(proto, "UDP") == 0))
646      {
647        if ((temp = pmlist_FindSpecific(ext_port, proto)))
648          result = pmlist_Delete(temp);
649
650          if (result==1)
651          {
652             trace(2, "DeletePortMap: Proto:%s Port:%s\n",proto, ext_port);
653             sprintf(num,"%d",pmlist_Size());
654             UpnpAddToPropertySet(&propSet,"PortMappingNumberOfEntries", num);
655             UpnpNotifyExt(deviceHandle, ca_event->DevUDN,ca_event->ServiceID,propSet);
656             ixmlDocument_free(propSet);
657             action_succeeded = 1;
658          }
659          else
660          {
661             trace(1, "Failure in GateDeviceDeletePortMapping: DeletePortMap: Proto:%s Port:%s\n",proto, ext_port);
662             ca_event->ErrCode = 714;
663             strcpy(ca_event->ErrStr, "NoSuchEntryInArray");
664             ca_event->ActionResult = NULL;
665          }
666       }
667       else
668       {
669          trace(1, "Failure in GateDeviceDeletePortMapping: Invalid NewProtocol=%s\n",proto);
670          ca_event->ErrCode = 402;
671                         strcpy(ca_event->ErrStr, "Invalid Args");
672                         ca_event->ActionResult = NULL;
673       }
674    }
675    else
676    {
677                 trace(1, "Failiure in GateDeviceDeletePortMapping: Invalid Arguments!");
678                 ca_event->ErrCode = 402;
679                 strcpy(ca_event->ErrStr, "Invalid Args");
680                 ca_event->ActionResult = NULL;
681    }
682
683    if (action_succeeded)
684    {
685       ca_event->ErrCode = UPNP_E_SUCCESS;
686       snprintf(resultStr, RESULT_LEN, "<u:%sResponse xmlns:u=\"%s\">\n%s\n</u:%sResponse>",
687          ca_event->ActionName, "urn:schemas-upnp-org:service:WANIPConnection:1", "", ca_event->ActionName);
688       ca_event->ActionResult = ixmlParseBuffer(resultStr);
689    }
690
691    if (ext_port) free(ext_port);
692    if (proto) free(proto);
693
694    return(ca_event->ErrCode);
695 }
696
697 // From sampleutil.c included with libupnp 
698 char* GetFirstDocumentItem( IN IXML_Document * doc,
699                                  IN const char *item )
700 {
701     IXML_NodeList *nodeList = NULL;
702     IXML_Node *textNode = NULL;
703     IXML_Node *tmpNode = NULL;
704
705     char *ret = NULL;
706
707     nodeList = ixmlDocument_getElementsByTagName( doc, ( char * )item );
708
709     if( nodeList ) {
710         if( ( tmpNode = ixmlNodeList_item( nodeList, 0 ) ) ) {
711             textNode = ixmlNode_getFirstChild( tmpNode );
712        if (textNode != NULL)
713        {
714       ret = strdup( ixmlNode_getNodeValue( textNode ) );
715        }
716         }
717     }
718
719     if( nodeList )
720         ixmlNodeList_free( nodeList );
721     return ret;
722 }
723
724 int ExpirationTimerThreadInit(void)
725 {
726   int retVal;
727   ThreadPoolAttr attr;
728   TPAttrInit( &attr );
729   TPAttrSetMaxThreads( &attr, MAX_THREADS );
730   TPAttrSetMinThreads( &attr, MIN_THREADS );
731   TPAttrSetJobsPerThread( &attr, JOBS_PER_THREAD );
732   TPAttrSetIdleTime( &attr, THREAD_IDLE_TIME );
733
734   if( ThreadPoolInit( &gExpirationThreadPool, &attr ) != UPNP_E_SUCCESS ) {
735     return UPNP_E_INIT_FAILED;
736   }
737
738   if( ( retVal = TimerThreadInit( &gExpirationTimerThread,
739                                   &gExpirationThreadPool ) ) !=
740       UPNP_E_SUCCESS ) {
741     return retVal;
742   }
743   
744   return 0;
745 }
746
747 int ExpirationTimerThreadShutdown(void)
748 {
749   return TimerThreadShutdown(&gExpirationTimerThread);
750 }
751
752
753 void free_expiration_event(expiration_event *event)
754 {
755   if (event->mapping!=NULL)
756     event->mapping->expirationEventId = -1;
757   free(event);
758 }
759
760 void ExpireMapping(void *input)
761 {
762   char num[5]; // Maximum number of port mapping entries 9999
763   IXML_Document *propSet = NULL;
764   expiration_event *event = ( expiration_event * ) input;
765     
766   ithread_mutex_lock(&DevMutex);
767
768   trace(2, "ExpireMapping: Proto:%s Port:%s\n",
769                       event->mapping->m_PortMappingProtocol, event->mapping->m_ExternalPort);
770
771   //reset the event id before deleting the mapping so that pmlist_Delete
772   //will not call CancelMappingExpiration
773   event->mapping->expirationEventId = -1;
774   pmlist_Delete(event->mapping);
775   
776   sprintf(num, "%d", pmlist_Size());
777   UpnpAddToPropertySet(&propSet, "PortMappingNumberOfEntries", num);
778   UpnpNotifyExt(deviceHandle, event->DevUDN, event->ServiceID, propSet);
779   ixmlDocument_free(propSet);
780   trace(3, "ExpireMapping: UpnpNotifyExt(deviceHandle,%s,%s,propSet)\n  PortMappingNumberOfEntries: %s",
781                       event->DevUDN, event->ServiceID, num);
782   
783   free_expiration_event(event);
784   
785   ithread_mutex_unlock(&DevMutex);
786 }
787
788 int ScheduleMappingExpiration(struct portMap *mapping, char *DevUDN, char *ServiceID)
789 {
790   int retVal = 0;
791   ThreadPoolJob job;
792   expiration_event *event;
793   time_t curtime = time(NULL);
794         
795   if (mapping->m_PortMappingLeaseDuration > 0) {
796     mapping->expirationTime = curtime + mapping->m_PortMappingLeaseDuration;
797   }
798   else {
799     //client did not provide a duration, so use the default duration
800     if (g_vars.duration==0) {
801       return 1; //no default duration set
802     }
803     else if (g_vars.duration>0) {
804       //relative duration
805       mapping->expirationTime = curtime+g_vars.duration;
806     }
807     else { //g_vars.duration < 0
808       //absolute daily expiration time
809       long int expclock = -1*g_vars.duration;
810       struct tm *loctime = localtime(&curtime);
811       long int curclock = loctime->tm_hour*3600 + loctime->tm_min*60 + loctime->tm_sec;
812       long int diff = expclock-curclock;
813       if (diff<60) //if exptime is in less than a minute (or in the past), schedule it in 24 hours instead
814         diff += 24*60*60;
815       mapping->expirationTime = curtime+diff;
816     }
817   }
818
819   event = ( expiration_event * ) malloc( sizeof( expiration_event ) );
820   if( event == NULL ) {
821     return 0;
822   }
823   event->mapping = mapping;
824   if (strlen(DevUDN) < sizeof(event->DevUDN)) strcpy(event->DevUDN, DevUDN);
825   else strcpy(event->DevUDN, "");
826   if (strlen(ServiceID) < sizeof(event->ServiceID)) strcpy(event->ServiceID, ServiceID);
827   else strcpy(event->ServiceID, "");
828   
829   TPJobInit( &job, ( start_routine ) ExpireMapping, event );
830   TPJobSetFreeFunction( &job, ( free_routine ) free_expiration_event );
831   if( ( retVal = TimerThreadSchedule( &gExpirationTimerThread,
832                                       mapping->expirationTime,
833                                       ABS_SEC, &job, SHORT_TERM,
834                                       &( event->eventId ) ) )
835       != UPNP_E_SUCCESS ) {
836     free( event );
837     mapping->expirationEventId = -1;
838     return 0;
839   }
840   mapping->expirationEventId = event->eventId;
841
842   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);
843
844   return event->eventId;
845 }
846
847 int CancelMappingExpiration(int expirationEventId)
848 {
849   ThreadPoolJob job;
850   if (expirationEventId<0)
851     return 1;
852   trace(3,"CancelMappingExpiration: eventId: %d",expirationEventId);
853   if (TimerThreadRemove(&gExpirationTimerThread,expirationEventId,&job)==0) {
854     free_expiration_event((expiration_event *)job.arg);
855   }
856   else {
857     trace(1,"  TimerThreadRemove failed!");
858   }
859   return 1;
860 }
861
862 void DeleteAllPortMappings(void)
863 {
864   IXML_Document *propSet = NULL;
865
866   ithread_mutex_lock(&DevMutex);
867
868   pmlist_FreeList();
869
870   UpnpAddToPropertySet(&propSet, "PortMappingNumberOfEntries", "0");
871   UpnpNotifyExt(deviceHandle, gateUDN, "urn:upnp-org:serviceId:WANIPConn1", propSet);
872   ixmlDocument_free(propSet);
873   trace(2, "DeleteAllPortMappings: UpnpNotifyExt(deviceHandle,%s,%s,propSet)\n  PortMappingNumberOfEntries: %s",
874         gateUDN, "urn:upnp-org:serviceId:WANIPConn1", "0");
875
876   ithread_mutex_unlock(&DevMutex);
877 }