Patch for WANIPv6FirewallControl:1 service
[igd2-for-linux:igd2-for-linux.git] / linuxigd2 / src / gatedevice.c
1 /** 
2  * This file is part of Nokia InternetGatewayDevice v2 reference implementation
3  * Copyright © 2009 Nokia Corporation and/or its subsidiary(-ies).
4  * Contact: mika.saaranen@nokia.com
5  * Developer(s): jaakko.pasanen@tieto.com, opensource@tieto.com
6  *  
7  * This file is part of igd2-for-linux project
8  * Copyright © 2011 France Telecom.
9  * Contact: fabrice.fontaine@orange-ftgroup.com
10  * Developer(s): fabrice.fontaine@orange-ftgroup.com, rmenard.ext@orange-ftgroup.com
11  * 
12  * This program is free software: you can redistribute it and/or modify 
13  * it under the terms of the GNU General Public License as published by 
14  * the Free Software Foundation, either version 2 of the License, or 
15  * (at your option) any later version. 
16  * 
17  * This program is distributed in the hope that it will be useful, 
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of 
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
20  * GNU General Public License for more details. 
21  * 
22  * You should have received a copy of the GNU General Public License 
23  * along with this program, see the /doc directory of this program. If 
24  * not, see http://www.gnu.org/licenses/. 
25  * 
26  */
27
28 #include <stdio.h>
29 #include <syslog.h>
30 #include <stdlib.h>
31 #include <upnp/ixml.h>
32 #include <string.h>
33 #include <time.h>
34 #include <upnp/upnp.h>
35 #include <upnp/upnptools.h>
36 #include <arpa/inet.h>
37 #include "globals.h"
38 #include "gatedevice.h"
39 #include "pmlist.h"
40 #include "lanhostconfig.h"
41 #include "wanipv6fw.h"
42 #include "config.h"
43
44 //Definitions for mapping expiration timer thread
45 static ThreadPool gExpirationThreadPool;
46 static ThreadPoolJob gEventUpdateJob;
47
48 static int gAutoDisconnectJobId = -1;
49
50 // MUTEX for locking shared state variables whenver they are changed
51 ithread_mutex_t DevMutex = PTHREAD_MUTEX_INITIALIZER;
52
53 // XML string definitions
54 static const char xml_portmapEntry[] =
55         "<p:PortMappingEntry>"
56         "<p:NewRemoteHost>%s</p:NewRemoteHost>"
57         "<p:NewExternalPort>%s</p:NewExternalPort>"
58         "<p:NewProtocol>%s</p:NewProtocol>"
59         "<p:NewInternalPort>%s</p:NewInternalPort>"
60         "<p:NewInternalClient>%s</p:NewInternalClient>"
61         "<p:NewEnabled>%d</p:NewEnabled>"
62         "<p:NewDescription>%s</p:NewDescription>"
63         "<p:NewLeaseTime>%li</p:NewLeaseTime>"
64         "</p:PortMappingEntry>\n";
65 static const char xml_portmapListingHeader[] =
66         "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
67         "<p:PortMappingList xmlns:p=\"urn:schemas-upnp-org:gw:WANIPConnection\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
68         "xsi:schemaLocation=\"urn:schemas-upnp-org:gw:WANIPConnection http://www.upnp.org/schemas/gw/WANIPConnection-v2.xsd\">\n";
69 static const char xml_portmapListingFooter[] = "</p:PortMappingList>";
70
71
72 /**
73  * Main event handler for callbacks from the SDK.  Determine type of event
74  * and dispatch to the appropriate handler (Note: Get Var Request deprecated)
75  *  
76  * @param EventType Type of event (UPNP_EVENT_SUBSCRIPTION_REQUEST, UPNP_CONTROL_GET_VAR_REQUEST, UPNP_CONTROL_ACTION_REQUEST).
77  * @param Event Upnp event struct.
78  * @param Cookie This parameter is not used.
79  * @return 0
80  */
81 int EventHandler(Upnp_EventType EventType, void *Event, void *Cookie)
82 {
83     switch (EventType)
84     {
85     case UPNP_EVENT_SUBSCRIPTION_REQUEST:
86         HandleSubscriptionRequest((struct Upnp_Subscription_Request *) Event);
87         break;
88         // -- Deprecated --
89     case UPNP_CONTROL_GET_VAR_REQUEST:
90         HandleGetVarRequest((struct Upnp_State_Var_Request *) Event);
91         break;
92     case UPNP_CONTROL_ACTION_REQUEST:
93         HandleActionRequest((struct Upnp_Action_Request *) Event);
94         break;
95     default:
96         trace(1, "Error in EventHandler: Unknown event type %d", EventType);
97     }
98     return (0);
99 }
100
101 /**
102  * Initialize state variables and parse device UDN's for InternetGatewayDevice, 
103  * WANDevice and WANConnectionDevice.
104  * Also read access level xml into memory.
105  *  
106  * @param descDocUrl Url of device description document.
107  * @return Upnp error code.
108  */
109 int StateTableInit(char *descDocUrl)
110 {
111     IXML_Document *ixmlDescDoc;
112     int ret;
113
114     if ((ret = UpnpDownloadXmlDoc(descDocUrl, &ixmlDescDoc)) != UPNP_E_SUCCESS)
115     {
116         syslog(LOG_ERR, "Could not parse description document. Exiting ...");
117         UpnpFinish();
118         exit(0);
119     }
120
121     // Get the UDN from the description document, then free the DescDoc's memory
122     // Assumes that order of devices in file is IGD, WAN, WANConn
123     gateUDN = GetDocumentItem(ixmlDescDoc, "UDN", 0);
124     wanUDN = GetDocumentItem(ixmlDescDoc, "UDN", 1);
125     wanConnectionUDN = GetDocumentItem(ixmlDescDoc, "UDN", 2);
126     ixmlDocument_free(ixmlDescDoc);
127
128     trace(3, "UDN's: %s\n%s\n%s\n",gateUDN,wanUDN,wanConnectionUDN);
129
130     if (gateUDN == NULL || wanUDN == NULL || wanConnectionUDN == NULL)
131     {
132         syslog(LOG_ERR, "Failed to get device UDN's from description document.  Exiting ...");
133         UpnpFinish();
134         exit(1);
135     }
136
137     // Initialize our linked list of port mappings.
138     pmlist_Head = pmlist_Current = NULL;
139
140     AutoDisconnectTime = 0;
141     IdleDisconnectTime = 0;
142     WarnDisconnectDelay = 0;
143     PortMappingNumberOfEntries = 0;
144     SystemUpdateID = 0;
145     setEthernetLinkStatus(EthernetLinkStatus, g_vars.extInterfaceName);
146     GetIpAddressStr(ExternalIPAddress, g_vars.extInterfaceName);
147     GetConnectionStatus(ConnectionStatus, g_vars.extInterfaceName);
148
149     if (!readStats(connection_stats))
150     {
151         syslog(LOG_ERR, "Failed get connection stats from /proc. Exiting ...");
152         UpnpFinish();
153         exit(1);
154     }
155     idle_time = 0;
156
157     // only supported type at the moment
158     strcpy(ConnectionType,"IP_Routed");
159
160     // Record the startup time, for uptime
161     startup_time = time(NULL);
162
163     return (ret);
164 }
165
166 void AcceptSubscriptionExtForIPv4AndIPv6(const char *DevID, const char *ServID,
167                                         IXML_Document *PropSet, Upnp_SID SubsId)
168 {
169     if(deviceHandle)
170         UpnpAcceptSubscriptionExt(deviceHandle, DevID, ServID, PropSet, SubsId);
171     if(deviceHandleIPv6)
172         UpnpAcceptSubscriptionExt(deviceHandleIPv6, DevID, ServID, PropSet,
173                 SubsId); 
174     if(deviceHandleIPv6UlaGua)
175         UpnpAcceptSubscriptionExt(deviceHandleIPv6UlaGua, DevID, ServID,
176                 PropSet, SubsId); 
177 }
178
179 void NotifyExtForIPv4AndIPv6(const char *DevID, const char *ServID,
180                             IXML_Document *PropSet)
181 {
182     if(deviceHandle)
183         UpnpNotifyExt(deviceHandle, DevID, ServID, PropSet);
184     if(deviceHandleIPv6)
185         UpnpNotifyExt(deviceHandleIPv6, DevID, ServID, PropSet);
186     if(deviceHandleIPv6UlaGua)
187         UpnpNotifyExt(deviceHandleIPv6UlaGua, DevID, ServID, PropSet);
188 }
189
190 /**
191  * Handles subscription request for state variable notifications.
192  *  
193  * @param sr_event Upnp Subscription Request struct
194  * @return 1
195  */
196 int HandleSubscriptionRequest(struct Upnp_Subscription_Request *sr_event)
197 {
198     IXML_Document *propSet = NULL;
199
200     ithread_mutex_lock(&DevMutex);
201
202     if (strcmp(sr_event->UDN, wanUDN) == 0)
203     {
204         // WAN Common Interface Config Device Notifications
205         if (strcmp(sr_event->ServiceId, "urn:upnp-org:serviceId:WANCommonIFC1") == 0)
206         {
207             trace(3, "Received request to subscribe to WANCommonIFC1");
208             UpnpAddToPropertySet(&propSet, "PhysicalLinkStatus", "Up");
209             AcceptSubscriptionExtForIPv4AndIPv6(sr_event->UDN, sr_event->ServiceId,
210                                                 propSet, sr_event->Sid);
211             ixmlDocument_free(propSet);
212         }
213     }
214     else if (strcmp(sr_event->UDN, wanConnectionUDN) == 0)
215     {
216         // WAN IP Connection Device Notifications
217         if (strcmp(sr_event->ServiceId, "urn:upnp-org:serviceId:WANIPConn1") == 0)
218         {
219             GetIpAddressStr(ExternalIPAddress, g_vars.extInterfaceName);
220             GetConnectionStatus(ConnectionStatus, g_vars.extInterfaceName);
221             trace(3, "Received request to subscribe to WANIPConn1");
222             UpnpAddToPropertySet(&propSet, "PossibleConnectionTypes","IP_Routed");
223             UpnpAddToPropertySet(&propSet, "ExternalIPAddress", ExternalIPAddress);
224             UpnpAddToPropertySet(&propSet, "ConnectionStatus", ConnectionStatus);
225
226             char tmp[11];
227             snprintf(tmp,11,"%ld",SystemUpdateID);
228             UpnpAddToPropertySet(&propSet, "SystemUpdateID",tmp);
229             snprintf(tmp,11,"%d",PortMappingNumberOfEntries);
230             UpnpAddToPropertySet(&propSet, "PortMappingNumberOfEntries",tmp);
231
232             AcceptSubscriptionExtForIPv4AndIPv6(sr_event->UDN, sr_event->ServiceId,
233                                                 propSet, sr_event->Sid);
234             ixmlDocument_free(propSet);
235         }
236         // LAN Host Config Management Notifications
237         else if (strcmp(sr_event->ServiceId, "urn:upnp-org:serviceId:LANHostConfig1") == 0)
238         {
239             trace(3, "Received request to subscribe to LANHostConfig1");
240             // No state variable requires eventing, is next step needed?
241             AcceptSubscriptionExtForIPv4AndIPv6(sr_event->UDN, sr_event->ServiceId,
242                                                 propSet, sr_event->Sid);
243             ixmlDocument_free(propSet);
244         }
245         else if (strcmp(sr_event->ServiceId, "urn:upnp-org:serviceId:WANEthLinkC1") == 0)
246         {
247             trace(3, "Received request to subscribe to WANEthLinkC1");
248             setEthernetLinkStatus(EthernetLinkStatus, g_vars.extInterfaceName);
249             UpnpAddToPropertySet(&propSet, "EthernetLinkStatus", EthernetLinkStatus);
250             AcceptSubscriptionExtForIPv4AndIPv6(sr_event->UDN, sr_event->ServiceId,
251                                                 propSet, sr_event->Sid);
252             ixmlDocument_free(propSet);
253         }
254         else if (strcmp(sr_event->ServiceId, "urn:upnp-org:serviceId:WANIPv6FwCtrl1") == 0)
255         {
256             trace(3, "Received request to subscribe to WANIPv6FwCtrl1 UDN : %s, SID : %s", sr_event->UDN, sr_event->Sid);
257             snprintf(FirewallEnabled,2,"%i",g_vars.ipv6firewallEnabled);
258             snprintf(InboundPinholeAllowed,2,"%i",g_vars.ipv6inboundPinholeAllowed);
259             UpnpAddToPropertySet(&propSet, "FirewallEnabled", FirewallEnabled);
260             UpnpAddToPropertySet(&propSet, "InboundPinholeAllowed", InboundPinholeAllowed);
261             AcceptSubscriptionExtForIPv4AndIPv6(sr_event->UDN, sr_event->ServiceId,
262                                                 propSet, sr_event->Sid);
263             ixmlDocument_free(propSet);
264         }
265     }
266     ithread_mutex_unlock(&DevMutex);
267     return(1);
268 }
269
270 /**
271  * Handles GetVar request for state variables.
272  * GET VAR REQUEST DEPRECATED FROM UPnP SPECIFICATIONS!
273  * Report this in debug and ignore requests.
274  *  
275  * @param sr_event Upnp GetVar Request struct
276  * @return 1
277  */
278 int HandleGetVarRequest(struct Upnp_State_Var_Request *gv_request)
279 {
280     //If anyone experiences problems please let us know.
281     trace(3, "Deprecated Get Variable Request received. Ignoring.");
282     return 1;
283 }
284
285 /**
286  * Handles action requests for WANCommonIFC1, WANIPConn1, LANHostConfig1 and
287  * WANEthLinkC1 services.
288  *  
289  * @param sr_event Upnp Action Request struct
290  * @return Upnp error code.
291  */
292 int HandleActionRequest(struct Upnp_Action_Request *ca_event)
293 {
294     int result = 0;
295
296     ithread_mutex_lock(&DevMutex);
297     trace(3, "ActionName = %s", ca_event->ActionName);
298
299     // check if CP is authorized to use this action.
300     // checking managed flag is left to action itself
301     if ( AuthorizeControlPoint(ca_event, 0, 1) == CONTROL_POINT_NOT_AUTHORIZED )
302     {
303         ithread_mutex_unlock(&DevMutex);
304         return ca_event->ErrCode;
305     }
306
307     if (strcmp(ca_event->DevUDN, wanUDN) == 0)
308     {
309         if (strcmp(ca_event->ServiceID,"urn:upnp-org:serviceId:WANCommonIFC1") == 0)
310         {
311             if (strcmp(ca_event->ActionName,"GetTotalBytesSent") == 0)
312             {
313                 if(GetNbSoapParameters(ca_event->ActionRequest, "u:GetTotalBytesSent") == 0) 
314                     result = GetTotal(ca_event, STATS_TX_BYTES);
315                 else
316                     addErrorData(ca_event, UPNP_SOAP_E_INVALID_ARGS, "Invalid Args");
317             }
318             else if (strcmp(ca_event->ActionName,"GetTotalBytesReceived") == 0)
319             {
320                 if(GetNbSoapParameters(ca_event->ActionRequest, "u:GetTotalBytesReceived") == 0)
321                     result = GetTotal(ca_event, STATS_RX_BYTES);
322                 else
323                     addErrorData(ca_event, UPNP_SOAP_E_INVALID_ARGS, "Invalid Args");
324             }
325             else if (strcmp(ca_event->ActionName,"GetTotalPacketsSent") == 0)
326             {
327                 if(GetNbSoapParameters(ca_event->ActionRequest, "u:GetTotalPacketsSent") == 0)
328                     result = GetTotal(ca_event, STATS_TX_PACKETS);
329                 else
330                     addErrorData(ca_event, UPNP_SOAP_E_INVALID_ARGS, "Invalid Args"); 
331             }
332             else if (strcmp(ca_event->ActionName,"GetTotalPacketsReceived") == 0)
333             {
334                 if(GetNbSoapParameters(ca_event->ActionRequest, "u:GetTotalPacketsReceived") == 0)
335                     result = GetTotal(ca_event, STATS_RX_PACKETS);
336                 else
337                     addErrorData(ca_event, UPNP_SOAP_E_INVALID_ARGS, "Invalid Args");
338             }
339             else if (strcmp(ca_event->ActionName,"GetCommonLinkProperties") == 0)
340                 result = GetCommonLinkProperties(ca_event);
341             else
342             {
343                 trace(1, "Invalid Action Request : %s",ca_event->ActionName);
344                 result = InvalidAction(ca_event);
345             }
346         }
347     }
348     else if (strcmp(ca_event->DevUDN, wanConnectionUDN) == 0)
349     {
350         if (strcmp(ca_event->ServiceID, "urn:upnp-org:serviceId:WANIPConn1") == 0)
351         {
352             if (strcmp(ca_event->ActionName,"GetConnectionTypeInfo") == 0)
353                 result = GetConnectionTypeInfo(ca_event);
354             else if (strcmp(ca_event->ActionName,"GetNATRSIPStatus") == 0)
355                 result = GetNATRSIPStatus(ca_event);
356             else if (strcmp(ca_event->ActionName,"SetConnectionType") == 0)
357                 result = SetConnectionType(ca_event);
358             else if (strcmp(ca_event->ActionName,"RequestConnection") == 0)
359                 result = RequestConnection(ca_event);
360             else if (strcmp(ca_event->ActionName,"AddPortMapping") == 0)
361                 result = AddPortMapping(ca_event);
362             else if (strcmp(ca_event->ActionName,"GetGenericPortMappingEntry") == 0)
363                 result = GetGenericPortMappingEntry(ca_event);
364             else if (strcmp(ca_event->ActionName,"GetSpecificPortMappingEntry") == 0)
365                 result = GetSpecificPortMappingEntry(ca_event);
366             else if (strcmp(ca_event->ActionName,"GetExternalIPAddress") == 0)
367                 result = GetExternalIPAddress(ca_event);
368             else if (strcmp(ca_event->ActionName,"DeletePortMapping") == 0)
369                 result = DeletePortMapping(ca_event);
370             else if (strcmp(ca_event->ActionName,"GetStatusInfo") == 0)
371                 result = GetStatusInfo(ca_event);
372             else if (strcmp(ca_event->ActionName,"DeletePortMappingRange") == 0)
373                 result = DeletePortMappingRange(ca_event);
374             else if (strcmp(ca_event->ActionName,"AddAnyPortMapping") == 0)
375                 result = AddAnyPortMapping(ca_event);
376             else if (strcmp(ca_event->ActionName,"GetListOfPortMappings") == 0)
377                 result = GetListOfPortmappings(ca_event);
378             else if (strcmp(ca_event->ActionName,"ForceTermination") == 0)
379                 result = ForceTermination(ca_event);
380             else if (strcmp(ca_event->ActionName,"RequestTermination") == 0)
381                 result = RequestTermination(ca_event);
382             else if (strcmp(ca_event->ActionName,"SetAutoDisconnectTime") == 0)
383                 result = SetAutoDisconnectTime(ca_event);
384             else if (strcmp(ca_event->ActionName,"SetIdleDisconnectTime") == 0)
385                 result = SetIdleDisconnectTime(ca_event);
386             else if (strcmp(ca_event->ActionName,"SetWarnDisconnectDelay") == 0)
387                 result = SetWarnDisconnectDelay(ca_event);
388             else if (strcmp(ca_event->ActionName,"GetAutoDisconnectTime") == 0)
389                 result = GetAutoDisconnectTime(ca_event);
390             else if (strcmp(ca_event->ActionName,"GetIdleDisconnectTime") == 0)
391                 result = GetIdleDisconnectTime(ca_event);
392             else if (strcmp(ca_event->ActionName,"GetWarnDisconnectDelay") == 0)
393                 result = GetWarnDisconnectDelay(ca_event);
394             else result = InvalidAction(ca_event);
395         }
396         else if (strcmp(ca_event->ServiceID,"urn:upnp-org:serviceId:LANHostConfig1") == 0)
397         {
398             if (strcmp(ca_event->ActionName,"SetDHCPServerConfigurable") == 0)
399                 result = SetDHCPServerConfigurable(ca_event);
400             else if (strcmp(ca_event->ActionName,"GetDHCPServerConfigurable") == 0)
401                 result = GetDHCPServerConfigurable(ca_event);
402             else if (strcmp(ca_event->ActionName,"SetDHCPRelay") == 0)
403                 result = SetDHCPRelay(ca_event);
404             else if (strcmp(ca_event->ActionName,"GetDHCPRelay") == 0)
405                 result = GetDHCPRelay(ca_event);
406             else if (strcmp(ca_event->ActionName,"SetSubnetMask") == 0)
407                 result = SetSubnetMask(ca_event);
408             else if (strcmp(ca_event->ActionName,"GetSubnetMask") == 0)
409                 result = GetSubnetMask(ca_event);
410             else if (strcmp(ca_event->ActionName,"SetIPRouter") == 0)
411                 result = SetIPRouter(ca_event);
412             else if (strcmp(ca_event->ActionName,"DeleteIPRouter") == 0)
413                 result = DeleteIPRouter(ca_event);
414             else if (strcmp(ca_event->ActionName,"GetIPRoutersList") == 0)
415                 result = GetIPRoutersList(ca_event);
416             else if (strcmp(ca_event->ActionName,"SetDomainName") == 0)
417                 result = SetDomainName(ca_event);
418             else if (strcmp(ca_event->ActionName,"GetDomainName") == 0)
419                 result = GetDomainName(ca_event);
420             else if (strcmp(ca_event->ActionName,"SetAddressRange") == 0)
421                 result = SetAddressRange(ca_event);
422             else if (strcmp(ca_event->ActionName,"GetAddressRange") == 0)
423                 result = GetAddressRange(ca_event);
424             else if (strcmp(ca_event->ActionName,"SetReservedAddress") == 0)
425                 result = SetReservedAddress(ca_event);
426             else if (strcmp(ca_event->ActionName,"DeleteReservedAddress") == 0)
427                 result = DeleteReservedAddress(ca_event);
428             else if (strcmp(ca_event->ActionName,"GetReservedAddresses") == 0)
429                 result = GetReservedAddresses(ca_event);
430             else if (strcmp(ca_event->ActionName,"SetDNSServer") == 0)
431                 result = SetDNSServer(ca_event);
432             else if (strcmp(ca_event->ActionName,"DeleteDNSServer") == 0)
433                 result = DeleteDNSServer(ca_event);
434             else if (strcmp(ca_event->ActionName,"GetDNSServers") == 0)
435                 result = GetDNSServers(ca_event);
436             else
437             {
438                 trace(1, "Action not supported: %s",ca_event->ActionName);
439                 result = InvalidAction(ca_event);
440             }
441         }
442         else if (strcmp(ca_event->ServiceID,"urn:upnp-org:serviceId:WANEthLinkC1") == 0)
443         {
444             if (strcmp(ca_event->ActionName,"GetEthernetLinkStatus") == 0)
445                 result = GetEthernetLinkStatus(ca_event);
446             else
447             {
448                 trace(1, "Invalid Action Request : %s",ca_event->ActionName);
449                 result = InvalidAction(ca_event);
450             }
451         }
452         /**
453          * Added for WANIPv6FirewallControl
454          */
455         else if (strcmp(ca_event->ServiceID,"urn:upnp-org:serviceId:WANIPv6FwCtrl1") == 0)
456         {
457             if (strcmp(ca_event->ActionName,"GetFirewallStatus") == 0)
458                 result = upnp_wanipv6_getFirewallStatus(ca_event);
459             else if (strcmp(ca_event->ActionName,"GetOutboundPinholeTimeout") == 0)
460                 result = upnp_wanipv6_getOutboundPinholeTimeOut(ca_event);
461             else if (strcmp(ca_event->ActionName,"AddPinhole") == 0)
462                 result = upnp_wanipv6_addPinhole(ca_event);
463             else if (strcmp(ca_event->ActionName,"UpdatePinhole") == 0)
464                 result = upnp_wanipv6_updatePinhole(ca_event);
465             else if (strcmp(ca_event->ActionName,"DeletePinhole") == 0)
466                 result = upnp_wanipv6_deletePinhole(ca_event);
467             else if (strcmp(ca_event->ActionName,"GetPinholePackets") == 0)
468                 result = upnp_wanipv6_getPinholePackets(ca_event);
469             else if (strcmp(ca_event->ActionName,"CheckPinholeWorking") == 0)
470                 result = upnp_wanipv6_checkPinholeWorking(ca_event);
471             else
472             {
473                 trace(1, "Invalid Action Request : %s",ca_event->ActionName);
474                 result = InvalidAction(ca_event);
475             }
476         }
477     }
478
479     ithread_mutex_unlock(&DevMutex);
480
481     return (result);
482 }
483
484 /**
485  * Default Action when we receive unknown Action Requests
486  *  
487  * @param sr_event Upnp Action Request struct
488  * @return Upnp error code 401.
489  */
490 int InvalidAction(struct Upnp_Action_Request *ca_event)
491 {
492     ca_event->ErrCode = 401;
493     strcpy(ca_event->ErrStr, "Invalid Action");
494     ca_event->ActionResult = NULL;
495     return (ca_event->ErrCode);
496 }
497
498 //-----------------------------------------------------------------------------
499 //
500 //                      WANCommonInterfaceConfig:1 Service Actions
501 //
502 //-----------------------------------------------------------------------------
503 /**
504  * WANCommonInterfaceConfig:1 Action: GetCommonLinkProperties
505  * 
506  * @param ca_event Upnp event struct.
507  * @return Upnp error code.
508  */
509 int GetCommonLinkProperties(struct Upnp_Action_Request *ca_event)
510 {
511     char resultStr[RESULT_LEN];
512     IXML_Document *result;
513
514     if(GetNbSoapParameters(ca_event->ActionRequest, "u:GetCommonLinkProperties") != 0)
515     {
516         trace(1, "GetCommonLinkProperties invalid number of parameters");
517         addErrorData(ca_event, UPNP_SOAP_E_INVALID_ARGS, "Invalid Args");
518         return ca_event->ErrCode;
519     }
520
521     ca_event->ErrCode = UPNP_E_SUCCESS;
522     snprintf(resultStr, RESULT_LEN,
523              "<u:GetCommonLinkPropertiesResponse xmlns:u=\"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1\">\n"
524              "<NewWANAccessType>Cable</NewWANAccessType>\n"
525              "<NewLayer1UpstreamMaxBitRate>%s</NewLayer1UpstreamMaxBitRate>\n"
526              "<NewLayer1DownstreamMaxBitRate>%s</NewLayer1DownstreamMaxBitRate>\n"
527              "<NewPhysicalLinkStatus>Up</NewPhysicalLinkStatus>\n"
528              "</u:GetCommonLinkPropertiesResponse>",g_vars.upstreamBitrate,g_vars.downstreamBitrate);
529
530     // Create a IXML_Document from resultStr and return with ca_event
531     if ((result = ixmlParseBuffer(resultStr)) != NULL)
532     {
533         ca_event->ActionResult = result;
534         ca_event->ErrCode = UPNP_E_SUCCESS;
535     }
536     else
537     {
538         trace(1, "Error parsing Response to GetCommonLinkProperties: %s", resultStr);
539         ca_event->ActionResult = NULL;
540         ca_event->ErrCode = 402;
541     }
542
543     return(ca_event->ErrCode);
544 }
545
546 /**
547  * WANCommonInterfaceConfig:1 Actions: GetTotalBytesSent
548  *                                     GetTotalBytesReceived
549  *                                     GetTotalPacketsSent
550  *                                     GetTotalPacketsReceived
551  * 
552  * Get specified statistic from /proc/net/dev.
553  * 
554  * @param ca_event Upnp event struct.
555  * @param stat Which value is read from /proc
556  * @return Upnp error code.
557  */
558 int GetTotal(struct Upnp_Action_Request *ca_event, stats_t stat)
559 {
560     char resultStr[RESULT_LEN];
561     const char *methods[STATS_LIMIT] =
562         { "BytesSent", "BytesReceived", "PacketsSent", "PacketsReceived" };
563     IXML_Document *result;
564     unsigned long stats[STATS_LIMIT];
565
566     if (!readStats(stats))
567     {
568         trace(1, "Error reading stats for GetTotal");
569         ca_event->ActionResult = NULL;
570         ca_event->ErrCode = 501;
571         return (ca_event->ErrCode);
572     }
573
574     snprintf(resultStr, RESULT_LEN,
575              "<u:GetTotal%sResponse xmlns:u=\"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1\">\n"
576              "<NewTotal%s>%lu</NewTotal%s>\n"
577              "</u:GetTotal%sResponse>",
578              methods[stat], methods[stat], stats[stat], methods[stat], methods[stat]);
579
580     // Create a IXML_Document from resultStr and return with ca_event
581     if ((result = ixmlParseBuffer(resultStr)) != NULL)
582     {
583         ca_event->ActionResult = result;
584         ca_event->ErrCode = UPNP_E_SUCCESS;
585     }
586     else
587     {
588         trace(1, "Error parsing response to GetTotal: %s", resultStr);
589         ca_event->ActionResult = NULL;
590         ca_event->ErrCode = 501;
591     }
592
593     return (ca_event->ErrCode);
594 }
595
596
597 //-----------------------------------------------------------------------------
598 //
599 //                      WANIPConnection:2 Service Actions
600 //
601 //-----------------------------------------------------------------------------
602
603 /**
604  * WANIPConnection:2 Action: GetStatusInfo
605  * 
606  * Returns connection status related information to the control points.
607  * 
608  * @param ca_event Upnp event struct.
609  * @return Upnp error code.
610  */
611 int GetStatusInfo(struct Upnp_Action_Request *ca_event)
612 {
613     long int uptime;
614     char resultStr[RESULT_LEN];
615     IXML_Document *result = NULL;
616
617     if(GetNbSoapParameters(ca_event->ActionRequest, "u:GetStatusInfo") != 0)
618     {
619         trace(1, "GetStatusInfo invalid number of parameters");
620         addErrorData(ca_event, UPNP_SOAP_E_INVALID_ARGS, "Invalid Args");
621         return ca_event->ErrCode;
622     }
623
624     // If connection is not connected, uptime value is 0
625     if (strcmp(ConnectionStatus, "Connected") == 0)
626         uptime = (time(NULL) - startup_time);
627     else
628         uptime = 0;
629
630     snprintf(resultStr, RESULT_LEN,
631              "<u:GetStatusInfoResponse xmlns:u=\"%s\">\n"
632              "<NewConnectionStatus>%s</NewConnectionStatus>\n"
633              "<NewLastConnectionError>ERROR_NONE</NewLastConnectionError>\n"
634              "<NewUptime>%ld</NewUptime>\n"
635              "</u:GetStatusInfoResponse>",
636              WANIP_SERVICE_TYPE, ConnectionStatus, uptime);
637
638     // Create a IXML_Document from resultStr and return with ca_event
639     if ((result = ixmlParseBuffer(resultStr)) != NULL)
640     {
641         ca_event->ActionResult = result;
642         ca_event->ErrCode = UPNP_E_SUCCESS;
643     }
644     else
645     {
646         trace(1, "Error parsing Response to GetStatusInfo: %s", resultStr);
647         ca_event->ActionResult = NULL;
648         ca_event->ErrCode = 402;
649     }
650
651     return(ca_event->ErrCode);
652 }
653
654 /**
655  * WANIPConnection:2 Action: GetConnectionTypeInfo
656  * 
657  * As IP_Routed is the only relevant Connection Type for Linux-IGD
658  * we respond with IP_Routed as both current type and only type
659  * 
660  * @param ca_event Upnp event struct.
661  * @return Upnp error code.
662  */
663 int GetConnectionTypeInfo (struct Upnp_Action_Request *ca_event)
664 {
665     char resultStr[RESULT_LEN];
666     IXML_Document *result;
667
668     if(GetNbSoapParameters(ca_event->ActionRequest, "u:GetConnectionTypeInfo") != 0) {
669         trace(1, "GetConnectionTypeInfo invalid number of parameters");
670         addErrorData(ca_event, UPNP_SOAP_E_INVALID_ARGS, "Invalid Args");
671         return ca_event->ErrCode;
672     }
673
674     snprintf(resultStr, RESULT_LEN,
675              "<u:GetConnectionTypeInfoResponse xmlns:u=\"%s\">\n"
676              "<NewConnectionType>IP_Routed</NewConnectionType>\n"
677              "<NewPossibleConnectionTypes>IP_Routed</NewPossibleConnectionTypes>"
678              "</u:GetConnectionTypeInfoResponse>", WANIP_SERVICE_TYPE);
679
680     // Create a IXML_Document from resultStr and return with ca_event
681     if ((result = ixmlParseBuffer(resultStr)) != NULL)
682     {
683         ca_event->ActionResult = result;
684         ca_event->ErrCode = UPNP_E_SUCCESS;
685     }
686     else
687     {
688         trace(1, "Error parsing Response to GetConnectionTypeinfo: %s", resultStr);
689         ca_event->ActionResult = NULL;
690         ca_event->ErrCode = 402;
691     }
692
693     return(ca_event->ErrCode);
694 }
695
696 /**
697  * WANIPConnection:2 Action: GetNATRSIPStatus
698  * 
699  * Linux-IGD does not support RSIP.  However NAT is of course so respond with NewNATEnabled = 1
700  * 
701  * @param ca_event Upnp event struct.
702  * @return Upnp error code.
703  */
704 int GetNATRSIPStatus(struct Upnp_Action_Request *ca_event)
705 {
706     char resultStr[RESULT_LEN];
707     IXML_Document *result;
708
709     if(GetNbSoapParameters(ca_event->ActionRequest, "u:GetNATRSIPStatus") != 0) {
710         trace(1, "GetNATRSIPStatus invalid number of parameters");
711         addErrorData(ca_event, UPNP_SOAP_E_INVALID_ARGS, "Invalid Args");
712         return ca_event->ErrCode;
713     }
714
715     snprintf(resultStr, RESULT_LEN, "<u:GetNATRSIPStatusResponse xmlns:u=\"%s\">\n"
716              "<NewRSIPAvailable>0</NewRSIPAvailable>\n"
717              "<NewNATEnabled>1</NewNATEnabled>\n"
718              "</u:GetNATRSIPStatusResponse>", WANIP_SERVICE_TYPE);
719
720     // Create a IXML_Document from resultStr and return with ca_event
721     if ((result = ixmlParseBuffer(resultStr)) != NULL)
722     {
723         ca_event->ActionResult = result;
724         ca_event->ErrCode = UPNP_E_SUCCESS;
725     }
726     else
727     {
728         trace(1, "Error parsing Response to GetNATRSIPStatus: %s", resultStr);
729         ca_event->ActionResult = NULL;
730         ca_event->ErrCode = 402;
731     }
732
733     return(ca_event->ErrCode);
734 }
735
736 /**
737  * WANIPConnection:2 Action: SetConnectionType
738  * 
739  * Connection Type is a Read Only Variable as linux-igd is only
740  * a device that supports a NATing IP router (not an Ethernet
741  * bridge).  Possible other uses may be explored.
742  * 
743  * @param ca_event Upnp event struct.
744  * @return Upnp error code.
745  */
746 int SetConnectionType(struct Upnp_Action_Request *ca_event)
747 {
748     if(GetNbSoapParameters(ca_event->ActionRequest, "u:SetConnectionType") != 1)
749     {
750         trace(1, "SetConnectionType invalid number of parameters");
751         addErrorData(ca_event, UPNP_SOAP_E_INVALID_ARGS, "Invalid Args");
752         return ca_event->ErrCode;
753     }
754
755     ca_event->ErrCode = 731;
756     strcpy(ca_event->ErrStr, "ReadOnly");
757     ca_event->ActionResult = NULL;
758
759     return ca_event->ErrCode;
760 }
761
762  /**
763  * WANIPConnection:2 Action: SetAutoDisconnectTime
764  * 
765  * This action sets value of the AutoDisconnectTime state variable.
766  * 
767  * @param ca_event Upnp event struct.
768  * @return Upnp error code.
769  */
770 int SetAutoDisconnectTime(struct Upnp_Action_Request *ca_event)
771 {
772     char *delay_str = NULL;
773     long int delay;
774     int result = 0;
775
776     if ((delay_str = GetFirstDocumentItem(ca_event->ActionRequest, "NewAutoDisconnectTime")) &&
777             (GetNbSoapParameters(ca_event->ActionRequest, "u:SetAutoDisconnectTime")==1))
778     {
779         delay = atol(delay_str);
780         if (delay < 0)
781         {
782             trace(1, "%s: Argument value out of range",ca_event->ActionName);
783             result = 601;
784             addErrorData(ca_event, 601, "Argument Value Out of Range");
785         }
786         else
787         {
788             AutoDisconnectTime = delay;
789             trace(2, "%s: WAN connection AutoDisconnectTime set to %ld seconds.",ca_event->ActionName, AutoDisconnectTime);
790         }
791
792         if (result == 0)
793         {
794             if (createAutoDisconnectTimer() == 0)
795             {
796                 // create response SOAP message
797                 IXML_Document *ActionResult = NULL;
798                 ActionResult = UpnpMakeActionResponse(ca_event->ActionName, WANIP_SERVICE_TYPE,
799                                                         0, NULL);
800                 ca_event->ActionResult = ActionResult;
801                 ca_event->ErrCode = UPNP_E_SUCCESS;
802             }
803             else
804             {
805                 trace(1, "%s: Failed to create AutoDisconnect timer",ca_event->ActionName);
806                 addErrorData(ca_event, 501, "Action Failed");
807             }
808         }
809     }
810     else
811     {
812         trace(1, "%s: Invalid Args",ca_event->ActionName);
813         addErrorData(ca_event, 402, "Invalid Args");
814     }
815     free (delay_str);
816     return (ca_event->ErrCode);
817 }
818
819  /**
820  * WANIPConnection:2 Action: SetIdleDisconnectTime
821  * 
822  * This action sets value of the IdleDisconnectTime state variable.
823  * 
824  * @param ca_event Upnp event struct.
825  * @return Upnp error code.
826  */
827 int SetIdleDisconnectTime(struct Upnp_Action_Request *ca_event)
828 {
829     char *delay_str = NULL;
830     long int delay;
831     int result = 0;
832
833     if ((delay_str = GetFirstDocumentItem(ca_event->ActionRequest, "NewIdleDisconnectTime")) &&
834             (GetNbSoapParameters(ca_event->ActionRequest, "u:SetIdleDisconnectTime")==1))
835     {
836         delay = atol(delay_str);
837         if (delay < 0)
838         {
839             trace(1, "%s: Argument value out of range",ca_event->ActionName);
840             result = 601;
841             addErrorData(ca_event, 601, "Argument Value Out of Range");
842         }
843         else
844         {
845             IdleDisconnectTime = delay;
846             trace(2, "%s: WAN connection IdleDisconnectTime set to %ld seconds.",ca_event->ActionName, IdleDisconnectTime);
847         }
848
849         if (result == 0)
850         {
851             // create response SOAP message
852             IXML_Document *ActionResult = NULL;
853             ActionResult = UpnpMakeActionResponse(ca_event->ActionName, WANIP_SERVICE_TYPE,
854                                             0, NULL);
855             ca_event->ActionResult = ActionResult;
856             ca_event->ErrCode = UPNP_E_SUCCESS;
857         }
858     }
859     else
860     {
861         trace(1, "%s: Invalid Args",ca_event->ActionName);
862         addErrorData(ca_event, 402, "Invalid Args");
863     }
864     free (delay_str);
865     return (ca_event->ErrCode);
866 }
867
868  /**
869  * WANIPConnection:2 Action: SetWarnDisconnectDelay
870  * 
871  * This action sets value of the WarnDisconnectDelay state variable.
872  * 
873  * @param ca_event Upnp event struct.
874  * @return Upnp error code.
875  */
876 int SetWarnDisconnectDelay(struct Upnp_Action_Request *ca_event)
877 {
878     char *delay_str = NULL;
879     long int delay;
880     int result = 0;
881
882     if ((delay_str = GetFirstDocumentItem(ca_event->ActionRequest, "NewWarnDisconnectDelay")) &&
883            (GetNbSoapParameters(ca_event->ActionRequest, "u:SetWarnDisconnectDelay")==1))
884     {
885         delay = atol(delay_str);
886         if (delay < 0)
887         {
888             trace(1, "%s: Argument value out of range",ca_event->ActionName);
889             result = 601;
890             addErrorData(ca_event, 601, "Argument Value Out of Range");
891         }
892         else
893         {
894             WarnDisconnectDelay = delay;
895             trace(2, "%s: WAN connection WarnDisconnectDelay set to %ld seconds.",ca_event->ActionName, WarnDisconnectDelay);
896         }
897
898         if (result == 0)
899         {
900             // create response SOAP message
901             IXML_Document *ActionResult = NULL;
902             ActionResult = UpnpMakeActionResponse(ca_event->ActionName, WANIP_SERVICE_TYPE,
903                                             0, NULL);
904             ca_event->ActionResult = ActionResult;
905             ca_event->ErrCode = UPNP_E_SUCCESS;
906         }
907
908     }
909     else
910     {
911         trace(1, "%s: Invalid Args",ca_event->ActionName);
912         addErrorData(ca_event, 402, "Invalid Args");
913     }
914     free (delay_str);
915     return (ca_event->ErrCode);
916 }
917
918  /**
919  * WANIPConnection:2 Action: GetAutoDisconnectTime
920  * 
921  * @param ca_event Upnp event struct.
922  * @return Upnp error code.
923  */
924 int GetAutoDisconnectTime(struct Upnp_Action_Request *ca_event)
925 {
926     IXML_Document *ActionResult = NULL;
927     char tmp[11];
928     snprintf(tmp,11,"%ld",AutoDisconnectTime);
929
930     if(GetNbSoapParameters(ca_event->ActionRequest, "u:GetAutoDisconnectTime") != 0)
931     {
932         trace(1, "GetAutoDisconnectTime invalid number of parameters");
933         addErrorData(ca_event, UPNP_SOAP_E_INVALID_ARGS, "Invalid Args");
934         return ca_event->ErrCode;
935     }
936
937     ActionResult = UpnpMakeActionResponse(ca_event->ActionName, WANIP_SERVICE_TYPE,
938                                           1,
939                                           "NewAutoDisconnectTime", tmp);
940
941     if (ActionResult)
942     {
943         ca_event->ActionResult = ActionResult;
944         ca_event->ErrCode = UPNP_E_SUCCESS;
945     }
946     else
947     {
948         trace(1, "Error parsing Response to %s",ca_event->ActionName);
949         addErrorData(ca_event, 501, "Action Failed");
950     }
951
952     return ca_event->ErrCode;
953 }
954
955  /**
956  * WANIPConnection:2 Action: GetIdleDisconnectTime
957  * 
958  * @param ca_event Upnp event struct.
959  * @return Upnp error code.
960  */
961 int GetIdleDisconnectTime(struct Upnp_Action_Request *ca_event)
962 {
963     IXML_Document *ActionResult = NULL;
964     char tmp[11];
965     snprintf(tmp,11,"%ld",IdleDisconnectTime);
966
967     if(GetNbSoapParameters(ca_event->ActionRequest, "u:GetIdleDisconnectTime") != 0)
968     {
969         trace(1, "GetIdleDisconnectTime invalid number of parameters");
970         addErrorData(ca_event, UPNP_SOAP_E_INVALID_ARGS, "Invalid Args");
971         return ca_event->ErrCode;
972     }
973
974     ActionResult = UpnpMakeActionResponse(ca_event->ActionName, WANIP_SERVICE_TYPE,
975                                           1,
976                                           "NewIdleDisconnectTime", tmp);
977
978     if (ActionResult)
979     {
980         ca_event->ActionResult = ActionResult;
981         ca_event->ErrCode = UPNP_E_SUCCESS;
982     }
983     else
984     {
985         trace(1, "Error parsing Response to %s",ca_event->ActionName);
986         addErrorData(ca_event, 501, "Action Failed");
987     }
988
989     return ca_event->ErrCode;
990 }
991
992  /**
993  * WANIPConnection:2 Action: GetWarnDisconnectDelay
994  * 
995  * @param ca_event Upnp event struct.
996  * @return Upnp error code.
997  */
998 int GetWarnDisconnectDelay(struct Upnp_Action_Request *ca_event)
999 {
1000     IXML_Document *ActionResult = NULL;
1001     char tmp[11];
1002     snprintf(tmp,11,"%ld",WarnDisconnectDelay);
1003
1004     if(GetNbSoapParameters(ca_event->ActionRequest, "u:GetWarnDisconnectDelay") != 0)
1005     {
1006         trace(1, "GetWarnDisconnectDelay invalid number of parameters");
1007         addErrorData(ca_event, UPNP_SOAP_E_INVALID_ARGS, "Invalid Args");
1008         return ca_event->ErrCode;
1009     }
1010
1011     ActionResult = UpnpMakeActionResponse(ca_event->ActionName, WANIP_SERVICE_TYPE,
1012                                           1,
1013                                           "NewWarnDisconnectDelay", tmp);
1014
1015     if (ActionResult)
1016     {
1017         ca_event->ActionResult = ActionResult;
1018         ca_event->ErrCode = UPNP_E_SUCCESS;
1019     }
1020     else
1021     {
1022         trace(1, "Error parsing Response to %s",ca_event->ActionName);
1023         addErrorData(ca_event, 501, "Action Failed");
1024     }
1025
1026     return ca_event->ErrCode;
1027 }
1028
1029 /**
1030  * WANIPConnection:2 Action: RequestConnection
1031  * 
1032  * Start DHCP Client and try to acquire IP-address.
1033  * If external interface has IP, assume that status is Connected, else Disconnected.
1034  * 
1035  * @param ca_event Upnp event struct.
1036  * @return Upnp error code.
1037  */
1038 int RequestConnection(struct Upnp_Action_Request *ca_event)
1039 {
1040     IXML_Document *propSet = NULL;
1041     int result = 0;
1042
1043     if(GetNbSoapParameters(ca_event->ActionRequest, "u:RequestConnection") != 0)
1044     {
1045         trace(1, "RequestConnection invalid number of parameters");
1046         addErrorData(ca_event, UPNP_SOAP_E_INVALID_ARGS, "Invalid Args");
1047         return ca_event->ErrCode;
1048     }
1049
1050     // create result document for succesfull cases. addErrorData overwrites this if no success
1051     IXML_Document *ActionResult = NULL;
1052     ActionResult = UpnpMakeActionResponse(ca_event->ActionName, WANIP_SERVICE_TYPE,
1053                                             0, NULL);
1054     ca_event->ActionResult = ActionResult;
1055     ca_event->ErrCode = UPNP_E_SUCCESS;
1056
1057     trace(2, "RequestConnection received ... Checking status...");
1058
1059     //Immediatley Set lastconnectionerror to none. We don't now care about errors.
1060     strcpy(LastConnectionError, "ERROR_NONE");
1061
1062     // connection already up. Nothing to do. Return success
1063     if (strcmp(ConnectionStatus,"Connected") == 0)
1064     {
1065         trace(2, "%s: Connection is already connected",ca_event->ActionName);
1066         return ca_event->ErrCode;
1067     }
1068     else if (strcmp(ConnectionType,"IP_Routed") != 0)
1069     {
1070         trace(1, "%s: ConnectionType must be IP_Routed. Type: %s",ca_event->ActionName, ConnectionType);
1071         result = 710;
1072         addErrorData(ca_event, result, "InvalidConnectionType");
1073     }
1074     else if (strcmp(ConnectionStatus,"Disconnecting") == 0)
1075     {
1076         trace(1, "%s: Connection of %s is disconnecting",ca_event->ActionName, g_vars.extInterfaceName);
1077         result = 707;
1078         addErrorData(ca_event, result, "DisconnectInProgress");
1079     }
1080     else if (strcmp(ConnectionStatus,"Connecting") == 0)
1081     {
1082         trace(1, "%s: Connection of %s is connecting",ca_event->ActionName, g_vars.extInterfaceName);
1083         result = 705;
1084         addErrorData(ca_event, result, "ConnectionSetupInProgress");
1085     }
1086     else if (strcmp(ConnectionStatus,"PendingDisconnect") == 0)
1087     {
1088         trace(1, "%s: Connection of %s is pending disconnect. Setting state back to Connected.",ca_event->ActionName, g_vars.extInterfaceName);
1089         strcpy(ConnectionStatus, "Connected");
1090         UpnpAddToPropertySet(&propSet, "ConnectionStatus", ConnectionStatus);
1091         NotifyExtForIPv4AndIPv6(ca_event->DevUDN, ca_event->ServiceID, propSet);
1092         ixmlDocument_free(propSet);
1093         propSet = NULL;
1094
1095         return ca_event->ErrCode;
1096     }
1097
1098     if (result == 0)
1099     {
1100         strcpy(ConnectionStatus, "Connecting");
1101         UpnpAddToPropertySet(&propSet, "ConnectionStatus", ConnectionStatus);
1102         NotifyExtForIPv4AndIPv6(ca_event->DevUDN, ca_event->ServiceID, propSet);
1103         ixmlDocument_free(propSet);
1104         propSet = NULL;
1105         trace(2, "RequestConnection received ... Connecting..");
1106
1107         if (startDHCPClient(g_vars.extInterfaceName))
1108             ca_event->ErrCode = UPNP_E_SUCCESS;
1109         else
1110         {
1111             trace(1, "%s: Connection set up failed",ca_event->ActionName, g_vars.extInterfaceName);
1112             result = 704;
1113             addErrorData(ca_event, result, "ConnectionSetupFailed");
1114         }
1115
1116         GetConnectionStatus(ConnectionStatus, g_vars.extInterfaceName);
1117         // Build DOM Document with state variable connectionstatus and event it
1118         UpnpAddToPropertySet(&propSet, "ConnectionStatus", ConnectionStatus);
1119         // Send off notifications of state change
1120         NotifyExtForIPv4AndIPv6(ca_event->DevUDN, ca_event->ServiceID, propSet);
1121
1122         // if new status is connected, we create autodisconnecttimer and set startup time for Uptime statevariable
1123         if (strcmp(ConnectionStatus, "Connected") == 0)
1124         {
1125             createAutoDisconnectTimer();
1126             // Record the startup time, for uptime
1127             startup_time = time(NULL);
1128         }
1129     }
1130
1131     ixmlDocument_free(propSet);
1132     return ca_event->ErrCode;
1133 }
1134
1135  /**
1136  * WANIPConnection:2 Action: ForceTermination
1137  * 
1138  * Force termination of WAN-connection immediatedly. (i.e. try to release IP of external interface 
1139  * by killing DHCP-client).
1140  * 
1141  * @param ca_event Upnp event struct.
1142  * @return Upnp error code.
1143  */
1144 int ForceTermination(struct Upnp_Action_Request *ca_event)
1145 {
1146     int result = 0;
1147
1148     if(GetNbSoapParameters(ca_event->ActionRequest, "u:ForceTermination") != 0)
1149     {
1150         trace(1, "ForceTermination invalid number of parameters");
1151         addErrorData(ca_event, UPNP_SOAP_E_INVALID_ARGS, "Invalid Args");
1152         return ca_event->ErrCode;
1153     }
1154
1155     if (strcmp(ConnectionStatus,"Disconnecting") == 0)
1156     {
1157         trace(1, "%s: Connection of %s already disconnecting", ca_event->ActionName, g_vars.extInterfaceName);
1158         result = 707;
1159         addErrorData(ca_event, result, "DisconnectInProgress");
1160     }
1161
1162     // if ok to continue termination
1163     if (result == 0)
1164     {
1165         return ConnectionTermination(ca_event, 0);
1166     }
1167
1168     return ca_event->ErrCode;
1169 }
1170
1171  /**
1172  * WANIPConnection:2 Action: RequestTermination
1173  * 
1174  * Terminate WAN connection after WarnDisconnectDelay.
1175  * 
1176  * @param ca_event Upnp event struct.
1177  * @return Upnp error code.
1178  */
1179 int RequestTermination(struct Upnp_Action_Request *ca_event)
1180 {
1181     int result = 0;
1182     long int delay = WarnDisconnectDelay;
1183
1184     if(GetNbSoapParameters(ca_event->ActionRequest, "u:RequestTermination") != 0)
1185     {
1186         trace(1, "RequestTermination invalid number of parameters");
1187         addErrorData(ca_event, UPNP_SOAP_E_INVALID_ARGS, "Invalid Args");
1188         return ca_event->ErrCode;
1189     }
1190
1191     if (strcmp(ConnectionStatus,"Disconnecting") == 0 || strcmp(ConnectionStatus,"PendingDisconnect") == 0) 
1192     {
1193         trace(1, "%s: Connection of %s already disconnecting", ca_event->ActionName, g_vars.extInterfaceName);
1194         result = 707;
1195         addErrorData(ca_event, result, "DisconnectInProgress");
1196     }
1197     else if (strcmp(ConnectionStatus,"Connecting") == 0) 
1198     {
1199         trace(3, "%s: Connection of %s Connecting. WarnDisconnectDelay is now ignored", ca_event->ActionName, g_vars.extInterfaceName);
1200         delay = 0;
1201     }
1202
1203     // if ok to continue termination
1204     if (result == 0)
1205     {
1206         return ConnectionTermination(ca_event, delay);
1207     }
1208
1209     return ca_event->ErrCode;
1210 }
1211
1212 /**
1213  * WANIPConnection:2 Action: AddPortMapping
1214  * 
1215  * Add New Port Map to the IGD
1216  * 
1217  * @param ca_event Upnp event struct.
1218  * @return Upnp error code.
1219  */
1220 int AddPortMapping(struct Upnp_Action_Request *ca_event)
1221 {
1222     char *remote_host=NULL;
1223     char *ext_port=NULL;
1224     char *proto=NULL;
1225     char *int_port=NULL;
1226     char *int_client=NULL;
1227     char *long_duration=NULL;
1228     char *bool_enabled=NULL;
1229     char *desc=NULL;
1230     struct portMap *ret;
1231     int result = 0;
1232     int update_portmap = 0;
1233
1234     if ( (remote_host = GetFirstDocumentItem(ca_event->ActionRequest, "NewRemoteHost") )
1235             && (ext_port = GetFirstDocumentItem(ca_event->ActionRequest, "NewExternalPort") )
1236             && (proto = GetFirstDocumentItem(ca_event->ActionRequest, "NewProtocol") )
1237             && (int_port = GetFirstDocumentItem(ca_event->ActionRequest, "NewInternalPort") )
1238             && (int_client = GetFirstDocumentItem(ca_event->ActionRequest, "NewInternalClient") )
1239             && (long_duration = GetFirstDocumentItem(ca_event->ActionRequest, "NewLeaseDuration") )
1240             && (bool_enabled = GetFirstDocumentItem(ca_event->ActionRequest, "NewEnabled") )
1241             && (isStringInteger(ext_port) )
1242             && (isStringInteger(int_port) )
1243             && (isStringInteger(long_duration) )
1244             && (GetNbSoapParameters(ca_event->ActionRequest, "u:AddPortMapping") == 8 )
1245             && (desc = GetFirstDocumentItem(ca_event->ActionRequest, "NewPortMappingDescription") ) )
1246     {
1247         if (((strcmp(proto, "TCP") != 0) && (strcmp(proto, "UDP") != 0))
1248             || (atoi(ext_port) < 0)
1249             || (atoi(int_port) < 1 || atoi(int_port) > 65535)
1250             || (atol(long_duration) < 0 || atol(long_duration) > 604800))
1251         {
1252             trace(1, "%s: Argument value out of range:  ExtPort: %s RemHost: %s Proto: %s IntPort: %s IntIP: %s Dur: %s Ena: %s Desc: %s",
1253                     ca_event->ActionName, ext_port, remote_host, proto, int_port, int_client, long_duration, bool_enabled, desc);
1254             result = 601;
1255             addErrorData(ca_event, result, "Argument Value Out of Range");
1256         }
1257         else if ( ((strcmp(remote_host, "") != 0) && !IsIpOrDomain(remote_host)) || !IsIpOrDomain(int_client) )
1258         {
1259             trace(1, "%s: RemoteHost or InternalClient Argument Value Invalid:  ExtPort: %s RemHost: %s Proto: %s IntPort: %s IntIP: %s Dur: %s Ena: %s Desc: %s",
1260                     ca_event->ActionName, ext_port, remote_host, proto, int_port, int_client, long_duration, bool_enabled, desc);
1261             result = 600;
1262             addErrorData(ca_event, result, "Argument Value Invalid");
1263         }
1264         // If ext_port or int_port is <1024 control point needs to be authorized
1265         else if ((atoi(ext_port) < 1024 || atoi(int_port) < 1024 || !ControlPointIP_equals_InternalClientIP(int_client, &ca_event->CtrlPtIPAddr))
1266              && AuthorizeControlPoint(ca_event, 1, 0) != CONTROL_POINT_AUTHORIZED)
1267         {
1268             trace(1, "Port numbers must be greater than 1023 and NewInternalClient must be same as IP of Control point \
1269 unless control port is authorized. external_port:%s, internal_port:%s internal_client:%s",
1270                   ext_port, int_port, int_client);
1271             result = 606;
1272             addErrorData(ca_event, result, "Action not authorized");
1273         }
1274
1275         // Check RemoteHost and InternalPort parameters
1276         else if (checkForWildCard(int_client)) 
1277         {
1278             trace(1, "Wild cards not permitted in internal_client:%s", int_client);
1279             result = 715;
1280             addErrorData(ca_event, result, "WildCardNotPermittedInSrcIp");
1281         }
1282         else if (checkForWildCard(ext_port)) // Not sure if this is really needed
1283         {
1284             trace(1, "Wild cards not permitted in external_port:%s", ext_port);
1285             result = 716;
1286             addErrorData(ca_event, result, "WildCardNotPermittedInExtPort");
1287         }
1288         else if (checkForWildCard(int_port)) 
1289         {
1290             trace(1, "Wild cards not permitted in internal_port:%s", int_port);
1291             result = 732;
1292             addErrorData(ca_event, result, "WildCardNotPermittedInIntPort");
1293         }
1294
1295         // parameters are OK
1296         if (result == 0)
1297         {
1298             // If port map with the same External Port, Protocol, Internal Client and remoteHost exists
1299             // then, as per spec, we overwrite it (for simplicity, we delete and re-add at end of list)
1300             // Note: This may cause problems with GetGernericPortMappingEntry if a CP expects the overwritten
1301             // to be in the same place.
1302             if ((ret = pmlist_Find(remote_host, ext_port, proto, int_client)) != NULL)
1303             {
1304                 trace(3, "Found port map to already exist for this client.  Replacing");
1305                 pmlist_Delete(ret);
1306                 update_portmap = 1;
1307             }
1308
1309             // If the ExternalPort and PortMappingProtocol pair is already mapped to another 
1310             // internal client, an error is returned.
1311             else if ((ret = pmlist_FindBy_extPort_proto(ext_port, proto)) != NULL && 
1312                     strcmp(ret->m_InternalClient, int_client) != 0)
1313             {
1314                 trace(1, "Portmapping with same external port '%s' and protocol '%s' are mapped to another client already.\n",ext_port,proto);
1315                 result = 718;
1316                 addErrorData(ca_event, result, "ConflictInMappingEntry");
1317             }
1318
1319             // if still no errors happened, add new portmapping
1320             if (result == 0)
1321                 result = AddNewPortMapping(ca_event,bool_enabled,atol(long_duration),remote_host,ext_port,int_port,proto,int_client,desc,update_portmap);
1322
1323             // create response message if success, AddNewPortMapping adds error to response if it fails
1324             if (result == 1)
1325             {
1326                 // create response SOAP message
1327                 IXML_Document *ActionResult = NULL;
1328                 ActionResult = UpnpMakeActionResponse(ca_event->ActionName, WANIP_SERVICE_TYPE,
1329                                                 0, NULL);
1330                 ca_event->ActionResult = ActionResult;
1331                 ca_event->ErrCode = UPNP_E_SUCCESS;
1332             }
1333         }
1334     }
1335     else
1336     {
1337         trace(1, "Failure in GateDeviceAddPortMapping: Invalid Arguments!");
1338         addErrorData(ca_event, 402, "Invalid Args");
1339     }
1340
1341     free(ext_port);
1342     free(int_port);
1343     free(proto);
1344     free(int_client);
1345     free(bool_enabled);
1346     free(desc);
1347     free(remote_host);
1348     free(long_duration);
1349
1350     return(ca_event->ErrCode);
1351 }
1352
1353  /**
1354  * WANIPConnection:2 Action: AddAnyPortMapping
1355  * 
1356  * Like AddPortMapping() action, AddAnyPortMapping() action also creates a port mapping 
1357  * specified with the same arguments. The behaviour differs only on the case where the 
1358  * specified port is not free, because in that case the gateway reserves any free 
1359  * NewExternalPort and NewProtocol pair and returns the NewReservedPort.
1360  * 
1361  * @param ca_event Upnp event struct.
1362  * @return Upnp error code.
1363  */
1364 int AddAnyPortMapping(struct Upnp_Action_Request *ca_event)
1365 {
1366     char *remote_host=NULL;
1367     char *ext_port=NULL;
1368     char *proto=NULL;
1369     char *int_port = NULL;
1370     char *int_client=NULL;
1371     char *bool_enabled=NULL;
1372     char *desc=NULL;
1373     char *long_duration=NULL;
1374     int next_free_port = 0;
1375     struct portMap *ret;
1376     int result = 0;
1377     char resultStr[RESULT_LEN];
1378     char freePort[5];
1379
1380     if ( (remote_host = GetFirstDocumentItem(ca_event->ActionRequest, "NewRemoteHost") )
1381             && (ext_port = GetFirstDocumentItem(ca_event->ActionRequest, "NewExternalPort") )
1382             && (proto = GetFirstDocumentItem(ca_event->ActionRequest, "NewProtocol") )
1383             && (int_port = GetFirstDocumentItem(ca_event->ActionRequest, "NewInternalPort") )
1384             && (int_client = GetFirstDocumentItem(ca_event->ActionRequest, "NewInternalClient") )
1385             && (bool_enabled = GetFirstDocumentItem(ca_event->ActionRequest, "NewEnabled") )
1386             && (desc = GetFirstDocumentItem(ca_event->ActionRequest, "NewPortMappingDescription") )
1387             && (long_duration = GetFirstDocumentItem(ca_event->ActionRequest, "NewLeaseDuration") )
1388             && (isStringInteger(ext_port) )
1389             && (isStringInteger(int_port) )
1390             && (isStringInteger(long_duration) )
1391             && (GetNbSoapParameters(ca_event->ActionRequest, "u:AddAnyPortMapping") == 8) )
1392     {
1393         if (((strcmp(proto, "TCP") != 0) && (strcmp(proto, "UDP") != 0))
1394             || (atoi(ext_port) < 0)
1395             || (atoi(int_port) < 1 && atoi(int_port) > 65535)
1396             || (atol(long_duration) < 0 && atol(long_duration) > 604800) )
1397         {
1398             trace(1, "%s: Argument value out of range:  ExtPort: %s RemHost: %s Proto: %s IntPort: %s IntIP: %s Dur: %s Ena: %s Desc: %s",
1399                     ca_event->ActionName, ext_port, remote_host, proto, int_port, int_client, long_duration, bool_enabled, desc);
1400             result = 601;
1401             addErrorData(ca_event, result, "Argument Value Out of Range");
1402         }
1403         else if ( ((strcmp(remote_host, "") != 0) && !IsIpOrDomain(remote_host)) || !IsIpOrDomain(int_client) )
1404         {
1405             trace(1, "%s: RemoteHost or InternalClient Argument Value Invalid:  ExtPort: %s RemHost: %s Proto: %s IntPort: %s IntIP: %s Dur: %s Ena: %s Desc: %s",
1406                     ca_event->ActionName, ext_port, remote_host, proto, int_port, int_client, long_duration, bool_enabled, desc);
1407             result = 600;
1408             addErrorData(ca_event, result, "Argument Value Invalid");
1409         }
1410         // If ext_port or int_port is <1024 control point needs to be authorized
1411         else if ((atoi(ext_port) < 1024 || atoi(int_port) < 1024 || !ControlPointIP_equals_InternalClientIP(int_client, &ca_event->CtrlPtIPAddr))
1412              && AuthorizeControlPoint(ca_event, 1, 0) != CONTROL_POINT_AUTHORIZED)
1413         {
1414             trace(1, "Port numbers must be greater than 1023 and NewInternalClient must be same as IP of Control point \
1415 unless control port is authorized. external_port:%s, internal_port:%s internal_client:%s",
1416                   ext_port, int_port, int_client);
1417             result = 606;
1418             addErrorData(ca_event, result, "Action not authorized");
1419         }
1420
1421         // Check Internal client and Port parameters
1422         else if (checkForWildCard(int_client)) 
1423         {
1424             trace(1, "Wild cards not permitted in internal_client:%s", int_client);
1425             result = 715;
1426             addErrorData(ca_event, result, "WildCardNotPermittedInSrcIp");
1427         }
1428         else if (checkForWildCard(ext_port))
1429         {
1430             trace(1, "Wild cards not permitted in external_port:%s", ext_port);
1431             result = 716;
1432             addErrorData(ca_event, result, "WildCardNotPermittedInExtPort");
1433         }
1434         else if (checkForWildCard(int_port)) 
1435         {
1436             trace(1, "Wild cards not permitted in internal_port:%s", int_port);
1437             result = 732;
1438             addErrorData(ca_event, result, "WildCardNotPermittedInIntPort");
1439         }
1440
1441         // Parameters OK... proceed with adding port map
1442         if (result == 0)
1443             {
1444                 // If port map with the same External Port, Protocol, Internal Client and RemoteHost exists
1445                 // we should just update it, NOT create new
1446                 if ((ret = pmlist_Find(remote_host, ext_port, proto, int_client)) != NULL)
1447                 {
1448                     trace(3, "Port map with same ExternalPort, Protocol, InternalClient and RemoteHost exists. Updating existing.");
1449                     // update existing (Remove old one and after that create new one with new values)
1450                     // TODO: Create functions which really updates existing portmappings found from iptables
1451                     //       Or at least find out if it is even possible.
1452                     pmlist_Delete(ret);
1453
1454                     result = AddNewPortMapping(ca_event, bool_enabled, atol(long_duration), remote_host,
1455                                                 ext_port, int_port, proto,
1456                                                 int_client, desc, 1);
1457                 }
1458                 // Else if port mapping using same external port and protocol,
1459                 // get new external port and create new port mapping
1460                 else if (!checkForWildCard(ext_port) && (ret = pmlist_FindBy_extPort_proto(ext_port, proto)) != NULL)
1461                 {
1462                     // Find searches free external port...
1463                     trace(3, "Port map with same ExternalPort and protocol exists. Finding next free ExternalPort...");
1464                     next_free_port = pmlist_FindNextFreePort(proto);
1465                     if (next_free_port > 0)
1466                     {
1467                         trace(3, "Found free port:%d", next_free_port);
1468                         sprintf(freePort, "%d", next_free_port);
1469                         result = AddNewPortMapping(ca_event, bool_enabled, atol(long_duration), remote_host,
1470                                                     freePort, int_port, proto,
1471                                                     int_client, desc, 0);
1472                     }
1473                     else 
1474                     {
1475                         result = 728; /* no free port found... use NoPortMapsAvailable error code */
1476                     }
1477                 }
1478                 else 
1479                 {
1480                     // Otherwise just add the port map
1481                     result = AddNewPortMapping(ca_event, bool_enabled, atol(long_duration), remote_host,
1482                                                 ext_port, int_port, proto,
1483                                                 int_client, desc, 0);
1484                 }
1485         }
1486         if (result==728)
1487         {
1488             trace(1,"Failure in GateDeviceAddAnyPortMapping: RemoteHost: %s Protocol:%s ExternalPort: %s InternalClient: %s.%s\n",
1489                     remote_host, proto, ext_port, int_client, int_port);
1490
1491             addErrorData(ca_event, 728, "NoPortMapsAvailable");
1492         }
1493
1494     }
1495     else
1496     {
1497         trace(1, "Failure in GateDeviceAddAnyPortMapping: Invalid Arguments!");
1498         trace(1, "  RemoteHost: %s ExternalPort: %s Protocol: %s InternalClient: %s Enabled: %s PortMappingDesc: %s LeaseDuration: %s",
1499                 remote_host, ext_port, proto, int_client, bool_enabled, desc, long_duration);
1500         addErrorData(ca_event, 402, "Invalid Args");
1501     }
1502
1503     if (result == 1)
1504     {
1505         ca_event->ErrCode = UPNP_E_SUCCESS;
1506         // Port mapping has been done for external port that control point wanted
1507         if (next_free_port == 0) next_free_port = atoi(ext_port);
1508
1509         snprintf(resultStr, RESULT_LEN, "<u:%sResponse xmlns:u=\"%s\">\n%s%d%s\n</u:%sResponse>",
1510                     ca_event->ActionName, WANIP_SERVICE_TYPE, "<NewReservedPort>",
1511                     next_free_port, "</NewReservedPort>", ca_event->ActionName);
1512         ca_event->ActionResult = ixmlParseBuffer(resultStr);
1513     }
1514
1515     free(remote_host);
1516     free(ext_port);
1517     free(proto);
1518     free(int_port);
1519     free(int_client);
1520     free(bool_enabled);
1521     free(desc);
1522     free(long_duration);
1523
1524     return(ca_event->ErrCode);
1525 }
1526
1527 /**
1528  * WANIPConnection:2 Action: GetGenericPortMappingEntry
1529  * 
1530  * This action retrieves NAT port mappings one entry at a time.
1531  * 
1532  * @param ca_event Upnp event struct.
1533  * @return Upnp error code.
1534  */
1535 int GetGenericPortMappingEntry(struct Upnp_Action_Request *ca_event)
1536 {
1537     char *mapindex = NULL;
1538     struct portMap *temp;
1539     char result_param[RESULT_LEN];
1540     char resultStr[RESULT_LEN];
1541     int action_succeeded = 0;
1542
1543     if ((mapindex = GetFirstDocumentItem(ca_event->ActionRequest, "NewPortMappingIndex"))
1544             && (GetNbSoapParameters(ca_event->ActionRequest, "u:GetGenericPortMappingEntry") == 1)
1545             && (isStringInteger(mapindex)) )
1546     {
1547         temp = pmlist_FindByIndex(atoi(mapindex));
1548         // if portmapping is found, we must check if CP is authorized OR if internalclient value of portmapping matches IP of CP
1549         // Also if CP is not authorized NewInternalPort and NewExternalPort values of the port mapping entry must be greater than or equal to 1024,
1550         // else empty values are returned 
1551         if (temp && (AuthorizeControlPoint(ca_event, 1, 0) == CONTROL_POINT_AUTHORIZED || 
1552                         (ControlPointIP_equals_InternalClientIP(temp->m_InternalClient, &ca_event->CtrlPtIPAddr) && 
1553                          atoi(temp->m_ExternalPort) > 1023 && atoi(temp->m_InternalPort) > 1023)
1554                      )
1555             )
1556         {
1557             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>",
1558                      temp->m_RemoteHost,
1559                      temp->m_ExternalPort,
1560                      temp->m_PortMappingProtocol,
1561                      temp->m_InternalPort,
1562                      temp->m_InternalClient,
1563                      temp->m_PortMappingEnabled,
1564                      temp->m_PortMappingDescription,
1565                      (temp->expirationTime-time(NULL)));
1566             action_succeeded = 1;
1567         }
1568         else if (!temp) // nothing in that index
1569         {
1570             trace(1, "GetGenericPortMappingEntry: SpecifiedArrayIndexInvalid");
1571             addErrorData(ca_event, 713, "SpecifiedArrayIndexInvalid");
1572         }
1573         else // not authorized and IP's doesn't match or too small portnumbers
1574         {
1575             trace(1, "GetGenericPortMappingEntry: Not authorized user and Control point IP and portmapping internal client doesn't mach or portnumbers of portmapping are under 1024");
1576             addErrorData(ca_event, 606, "Action not authorized");
1577         }
1578
1579         if (action_succeeded)
1580         {
1581             ca_event->ErrCode = UPNP_E_SUCCESS;
1582             snprintf(resultStr, RESULT_LEN, "<u:%sResponse xmlns:u=\"%s\">\n%s\n</u:%sResponse>", ca_event->ActionName,
1583                      WANIP_SERVICE_TYPE,result_param, ca_event->ActionName);
1584             ca_event->ActionResult = ixmlParseBuffer(resultStr);
1585             trace(3, ixmlPrintDocument(ca_event->ActionResult));
1586         }
1587
1588     }
1589     else
1590     {
1591         trace(1, "Failure in GetGenericPortMappingEntry: Invalid Args");
1592         addErrorData(ca_event, 402, "Invalid Args");
1593     }
1594     free (mapindex);
1595     return (ca_event->ErrCode);
1596 }
1597
1598 /**
1599  * WANIPConnection:2 Action: GetSpecificPortMappingEntry
1600  * 
1601  * This action reports the port mapping specified by the unique tuple of RemoteHost, 
1602  * ExternalPort and PortMappingProtocol.
1603  * 
1604  * @param ca_event Upnp event struct.
1605  * @return Upnp error code.
1606  */
1607 int GetSpecificPortMappingEntry(struct Upnp_Action_Request *ca_event)
1608 {
1609     char *remote_host=NULL;
1610     char *ext_port=NULL;
1611     char *proto=NULL;
1612     char result_param[RESULT_LEN];
1613     char resultStr[RESULT_LEN];
1614     int action_succeeded = 0;
1615     struct portMap *temp;
1616     int authorized = 0;
1617
1618     if ((remote_host = GetFirstDocumentItem(ca_event->ActionRequest, "NewRemoteHost")) &&
1619             (ext_port = GetFirstDocumentItem(ca_event->ActionRequest, "NewExternalPort")) &&
1620             (GetNbSoapParameters(ca_event->ActionRequest, "u:GetSpecificPortMappingEntry")==3) &&
1621             (isStringInteger(ext_port)) &&
1622             (proto = GetFirstDocumentItem(ca_event->ActionRequest,"NewProtocol")) )
1623     {
1624         //check if authorized
1625         if (AuthorizeControlPoint(ca_event, 1, 0) == CONTROL_POINT_AUTHORIZED)
1626         {
1627             authorized = 1;
1628         }
1629
1630         if (((strcmp(proto, "TCP") != 0) && (strcmp(proto, "UDP") != 0)) || 
1631             (atoi(ext_port) < 0) )
1632         {
1633             trace(1, "%s: Argument value out of range",ca_event->ActionName);
1634             addErrorData(ca_event, 601, "Argument Value Out of Range");
1635         }
1636         else if ((strcmp(remote_host, "") != 0) && !IsIpOrDomain(remote_host))
1637         {
1638             trace(1, "%s: Argument Value Invalid");
1639             addErrorData(ca_event, 600, "Argument Value Invalid");
1640         }
1641         else if (!authorized && (atoi(ext_port) < 1024))
1642         {
1643             trace(1, "Failure in GetSpecificPortMappingEntry: Action not authorized\n");
1644             addErrorData(ca_event, 606, "Action not authorized");
1645         }
1646         // if portmapping is found, we must check if CP is authorized OR if internalclient value of portmapping matches IP of CP
1647         // Also if CP is not authorized NewInternalPort and NewExternalPort values of the port mapping entry must be greater than or equal to 1024,
1648         // else error is returned 
1649         else if ((temp = pmlist_FindSpecific (remote_host, ext_port, proto)) && (authorized || 
1650                         (ControlPointIP_equals_InternalClientIP(temp->m_InternalClient, &ca_event->CtrlPtIPAddr) && 
1651                          atoi(temp->m_ExternalPort) > 1023 && atoi(temp->m_InternalPort) > 1023)
1652                      )
1653             )
1654         {
1655             snprintf(result_param, RESULT_LEN, "<NewInternalPort>%s</NewInternalPort><NewInternalClient>%s</NewInternalClient><NewEnabled>%d</NewEnabled><NewPortMappingDescription>%s</NewPortMappingDescription><NewLeaseDuration>%li</NewLeaseDuration>",
1656                      temp->m_InternalPort,
1657                      temp->m_InternalClient,
1658                      temp->m_PortMappingEnabled,
1659                      temp->m_PortMappingDescription,
1660                      (temp->expirationTime-time(NULL)));
1661             action_succeeded = 1;
1662         }
1663         else if (!temp)
1664         {
1665             trace(2, "GateDeviceGetSpecificPortMappingEntry: PortMapping Doesn't Exist...");
1666             addErrorData(ca_event, 714, "NoSuchEntryInArray");
1667         }
1668         else
1669         {
1670             trace(1, "Failure in GetSpecificPortMappingEntry: Action not authorized\n");
1671             addErrorData(ca_event, 606, "Action not authorized");
1672         }
1673
1674         if (action_succeeded)
1675         {
1676             ca_event->ErrCode = UPNP_E_SUCCESS;
1677             snprintf(resultStr, RESULT_LEN, "<u:%sResponse xmlns:u=\"%s\">\n%s\n</u:%sResponse>", ca_event->ActionName,
1678                      WANIP_SERVICE_TYPE,result_param, ca_event->ActionName);
1679             ca_event->ActionResult = ixmlParseBuffer(resultStr);
1680         }
1681     }
1682     else
1683     {
1684         trace(1, "Failure in GetSpecificPortMappingEntry: Invalid Args %s", remote_host);
1685         addErrorData(ca_event, 402, "Invalid Args");
1686     }
1687
1688     free(remote_host);
1689     free(ext_port);
1690     free(proto);
1691
1692     return (ca_event->ErrCode);
1693 }
1694
1695 /**
1696  * WANIPConnection:2 Action: GetExternalIPAddress
1697  * 
1698  * This action retrieves the value of the external IP address on this connection instance.
1699  * 
1700  * @param ca_event Upnp event struct.
1701  * @return Upnp error code.
1702  */
1703 int GetExternalIPAddress(struct Upnp_Action_Request *ca_event)
1704 {
1705     char resultStr[RESULT_LEN];
1706     IXML_Document *result = NULL;
1707
1708     if(GetNbSoapParameters(ca_event->ActionRequest, "u:GetExternalIPAddress") != 0)
1709     {
1710         trace(1, "Failure in GetExternalIPAddress: Invalid Args");
1711         addErrorData(ca_event, UPNP_SOAP_E_INVALID_ARGS, "Invalid Args");
1712         return ca_event->ErrCode;
1713     }
1714
1715     ca_event->ErrCode = UPNP_E_SUCCESS;
1716     GetIpAddressStr(ExternalIPAddress, g_vars.extInterfaceName);
1717     snprintf(resultStr, RESULT_LEN, "<u:GetExternalIPAddressResponse xmlns:u=\"%s\">\n"
1718              "<NewExternalIPAddress>%s</NewExternalIPAddress>\n"
1719              "</u:GetExternalIPAddressResponse>", WANIP_SERVICE_TYPE, ExternalIPAddress);
1720
1721     // Create a IXML_Document from resultStr and return with ca_event
1722     if ((result = ixmlParseBuffer(resultStr)) != NULL)
1723     {
1724         ca_event->ActionResult = result;
1725         ca_event->ErrCode = UPNP_E_SUCCESS;
1726     }
1727     else
1728     {
1729         trace(1, "Error parsing Response to ExternalIPAddress: %s", resultStr);
1730         addErrorData(ca_event, 402, "");
1731     }
1732
1733     return(ca_event->ErrCode);
1734 }
1735
1736 /**
1737  * WANIPConnection:2 Action: DeletePortMapping
1738  * 
1739  * This action deletes a previously instantiated port mapping. 
1740  * 
1741  * @param ca_event Upnp event struct.
1742  * @return Upnp error code.
1743  */
1744 int DeletePortMapping(struct Upnp_Action_Request *ca_event)
1745 {
1746     char *remote_host=NULL;
1747     char *ext_port=NULL;
1748     char *proto=NULL;
1749     int result=0;
1750     char num[5];
1751     char resultStr[RESULT_LEN];
1752     IXML_Document *propSet= NULL;
1753     int action_succeeded = 0;
1754     struct portMap *temp;
1755     char tmp[11];
1756     int authorized = 0;
1757
1758     if ((remote_host = GetFirstDocumentItem(ca_event->ActionRequest, "NewRemoteHost")) &&
1759             (ext_port = GetFirstDocumentItem(ca_event->ActionRequest, "NewExternalPort")) &&
1760             (GetNbSoapParameters(ca_event->ActionRequest, "u:DeletePortMapping")==3) &&
1761             (isStringInteger(ext_port)) &&
1762             (proto = GetFirstDocumentItem(ca_event->ActionRequest, "NewProtocol")) )
1763     {
1764         if (((strcmp(proto, "TCP") != 0) && (strcmp(proto, "UDP") != 0)) || 
1765             (atoi(ext_port) < 0) )
1766         {
1767             trace(1, "%s: Argument value out of range",ca_event->ActionName);
1768             result = 601;
1769             addErrorData(ca_event, result, "Argument Value Out of Range");
1770         }
1771         else if ((strcmp(remote_host, "") != 0) && !IsIpOrDomain(remote_host))
1772         {
1773             trace(1, "%s: Argument Value Invalid");
1774             result = 600;
1775             addErrorData(ca_event, result, "Argument Value Invalid");
1776         }
1777         //check if authorized
1778         if (AuthorizeControlPoint(ca_event, 1, 0) == CONTROL_POINT_AUTHORIZED)
1779         {
1780             authorized = 1;
1781         }
1782
1783         // check that external port value is greater than or equal to 1024 if CP is not authorized
1784         if (result == 0 && !authorized && atoi(ext_port) < 1024)
1785         {
1786             trace(1, "Failure 'Action not authorized' in DeletePortMapping: Remote Host:%s Proto:%s Port:%s. Port value is under 1024 and CP is not authorized\n",remote_host, proto, ext_port);
1787             addErrorData(ca_event, 606, "Action not authorized");
1788         }
1789         // if portmapping is found, we must check if CP is authorized OR if internalclient value of portmapping matches IP of CP
1790         // Also if CP is not authorized NewInternalPort and NewExternalPort values of the port mapping entry must be greater than or equal to 1024,
1791         // else error is returned 
1792         else if ((temp = pmlist_FindSpecific(remote_host, ext_port, proto)) != NULL && 
1793                      (authorized || 
1794                         (ControlPointIP_equals_InternalClientIP(temp->m_InternalClient, &ca_event->CtrlPtIPAddr) && 
1795                          atoi(temp->m_ExternalPort) > 1023 && atoi(temp->m_InternalPort) > 1023))
1796             )
1797         {
1798             result = pmlist_Delete(temp);
1799
1800             if (result==1)
1801             {
1802                 trace(2, "DeletePortMap: Remote Host: %s Proto:%s Port:%s\n", remote_host, proto, ext_port);
1803                 PortMappingNumberOfEntries = pmlist_Size();
1804                 sprintf(num,"%d",PortMappingNumberOfEntries);
1805                 UpnpAddToPropertySet(&propSet,"PortMappingNumberOfEntries", num);
1806                 snprintf(tmp,11,"%ld",++SystemUpdateID);
1807                 UpnpAddToPropertySet(&propSet,"SystemUpdateID", tmp);
1808                 NotifyExtForIPv4AndIPv6(ca_event->DevUDN, ca_event->ServiceID, propSet);
1809                 ixmlDocument_free(propSet);
1810                 action_succeeded = 1;
1811             }
1812             else
1813             {
1814                 trace(2, "%s: Failed to remove portmapping.", ca_event->ActionName);
1815                 // add error to ca_event
1816                 addErrorData(ca_event, 501, "Action Failed");
1817             }
1818         }
1819         else if (!temp)
1820         {
1821             trace(1, "Failure 'NoSuchEntryInArray' in DeletePortMapping: Remote Host:%s Proto:%s Port:%s\n",remote_host, proto, ext_port);
1822             addErrorData(ca_event, 714, "NoSuchEntryInArray");
1823         }
1824         else
1825         {
1826             trace(1, "Failure 'Action not authorized' in DeletePortMapping: Remote Host:%s Proto:%s Port:%s\n",remote_host, proto, ext_port);
1827             addErrorData(ca_event, 606, "Action not authorized");
1828         }
1829     }
1830     else
1831     {
1832         trace(1, "Failure in GateDeviceDeletePortMapping: Invalid Arguments!");
1833         addErrorData(ca_event, 402, "Invalid Args");
1834     }
1835
1836     if (action_succeeded)
1837     {
1838         ca_event->ErrCode = UPNP_E_SUCCESS;
1839         snprintf(resultStr, RESULT_LEN, "<u:%sResponse xmlns:u=\"%s\">\n%s\n</u:%sResponse>",
1840                  ca_event->ActionName, WANIP_SERVICE_TYPE, "", ca_event->ActionName);
1841         ca_event->ActionResult = ixmlParseBuffer(resultStr);
1842     }
1843
1844     free(remote_host);
1845     free(ext_port);
1846     free(proto);
1847
1848     return(ca_event->ErrCode);
1849 }
1850
1851 /**
1852  * WANIPConnection:2 Action: DeletePortMappingRange
1853  * 
1854  * This action deletes port mapping entries defined by a range.
1855  * 
1856  * @param ca_event Upnp event struct.
1857  * @return Upnp error code.
1858  */
1859 int DeletePortMappingRange(struct Upnp_Action_Request *ca_event)
1860 {
1861     char *start_port=NULL;
1862     char *end_port=NULL;
1863     char *proto=NULL;
1864     char *bool_manage=NULL;
1865     int start=0;
1866     int end=0;
1867     int ext_port=0;
1868     int result=0;
1869     int str_len = 6;
1870     char del_port[str_len];
1871     char tmp[11];
1872     char resultStr[RESULT_LEN];
1873     IXML_Document *propSet= NULL;
1874     int action_succeeded = 0;
1875     struct portMap *temp;
1876     int authorized = 0;
1877     int managed = 0;
1878     int index = 0;
1879     int foundPortmapCount = 0;
1880
1881     ca_event->ErrCode = UPNP_E_SUCCESS;
1882
1883     if ((start_port = GetFirstDocumentItem(ca_event->ActionRequest, "NewStartPort")) &&
1884             (end_port = GetFirstDocumentItem(ca_event->ActionRequest, "NewEndPort")) &&
1885             (proto = GetFirstDocumentItem(ca_event->ActionRequest, "NewProtocol")) &&
1886             (GetNbSoapParameters(ca_event->ActionRequest, "u:DeletePortMappingRange") == 4) &&
1887             (isStringInteger(start_port)) &&
1888             (isStringInteger(end_port)) &&
1889             (bool_manage = GetFirstDocumentItem(ca_event->ActionRequest, "NewManage")) )
1890     {
1891         //check if authorized
1892         if (AuthorizeControlPoint(ca_event, 1, 0) == CONTROL_POINT_AUTHORIZED)
1893         {
1894             authorized = 1;
1895         }
1896
1897         if (((strcmp(proto, "TCP") != 0) && (strcmp(proto, "UDP") != 0)) || 
1898             (atoi(start_port) < 0) ||
1899             (atoi(end_port) < 0) )
1900         {
1901             trace(1, "%s: Argument value out of range",ca_event->ActionName);
1902             result = 601;
1903             addErrorData(ca_event, result, "Argument Value Out of Range");
1904         }
1905         // check that port values are greater than or equal to 1024 if CP is not authorized
1906         else if (!authorized && (atoi(start_port) < 1024 || atoi(end_port) < 1024))
1907         {
1908             trace(1, "Failure in DeletePortMappingRange: StartPort:%s EndPort:%s Proto:%s Manage:%s. Port values under 1024 and CP is not authorized\n",start_port,end_port,proto,bool_manage);
1909             addErrorData(ca_event, 606, "Action not authorized");
1910         }
1911         else if ((end = atoi(end_port)) < (start = atoi(start_port)))
1912         {
1913             trace(1, "Failure in DeletePortMappingRange: StartPort:%s EndPort:%s Proto:%s Manage:%s InconsistentParameters!\n", start_port,end_port,proto,bool_manage);
1914             addErrorData(ca_event, 733, "InconsistentParameters");
1915         }
1916
1917         // parameters OK, lets continue
1918         if (ca_event->ErrCode == UPNP_E_SUCCESS) 
1919         {
1920             managed = resolveBoolean(bool_manage);
1921
1922             //loop ports from start to end
1923             for (ext_port = start; ext_port <= end; ext_port++)
1924             {
1925                 snprintf(del_port,str_len,"%d",ext_port);
1926                 index = 0;
1927                 // remove all instances with externalPort, actually there can ony be one, byt let's be sure
1928                 while ( (temp = pmlist_FindBy_extPort_proto_afterIndex(del_port, proto, index)) != NULL )
1929                 {
1930                     foundPortmapCount++;
1931                     // portmapping can be deleted if control point IP is same as internal client of portmapping,
1932                     // or if user is authorized and managed flag is up
1933                     if ((authorized && managed) || ControlPointIP_equals_InternalClientIP(temp->m_InternalClient, &ca_event->CtrlPtIPAddr))
1934                     {
1935                         // delete portmapping
1936                         result = pmlist_Delete(temp);
1937
1938                         if (result==1)
1939                         {
1940                             trace(2, "DeletePortMappingRange: DeletedPort:%s StartPort:%s EndPort:%s  Proto:%s Manage:%s\n", del_port, start_port, end_port, proto, bool_manage);
1941                             action_succeeded = 1;
1942                         }
1943                     }
1944                     else // if portmap is deleted, index of following port mappings decreases, that is why we increase our index only when nothing is removed
1945                         index++;
1946                 }
1947             }
1948
1949             // if action has succeeded and something has been deleted, send event and update SystemUpdateId 
1950             if (action_succeeded)
1951             {
1952                 SystemUpdateID++;
1953                 PortMappingNumberOfEntries = pmlist_Size();
1954                 sprintf(tmp,"%d",PortMappingNumberOfEntries);
1955                 UpnpAddToPropertySet(&propSet,"PortMappingNumberOfEntries", tmp);
1956                 snprintf(tmp,11,"%ld",SystemUpdateID);
1957                 UpnpAddToPropertySet(&propSet,"SystemUpdateID", tmp);
1958                 NotifyExtForIPv4AndIPv6(ca_event->DevUDN, ca_event->ServiceID, propSet);
1959             }
1960
1961             // portmappings which are in area of deletion exists, but none has been deleted -> Action is not authorized
1962             else if (foundPortmapCount > 0 && !action_succeeded)
1963             {
1964                 trace(1, "Failure in DeletePortMappingRange: StartPort:%s EndPort:%s Proto:%s Manage:%s Action not authorized!\n", start_port,end_port,proto,bool_manage);
1965                 addErrorData(ca_event, 606, "Action not authorized");
1966             }
1967             else if (!action_succeeded) // there just is not any portmaps to match given parameters
1968             {
1969                 trace(1, "Failure in DeletePortMappingRange: StartPort:%s EndPort:%s Proto:%s Manage:%s NoSuchEntryInArray!\n", start_port,end_port,proto,bool_manage);
1970                 addErrorData(ca_event, 730, "PortMappingNotFound"); 
1971             }
1972         }
1973     }
1974     else
1975     {
1976         trace(1, "Failure in DeletePortMappingRange: Invalid Arguments!");
1977         addErrorData(ca_event, 402, "Invalid Args");
1978     }
1979
1980     if (action_succeeded)
1981     {
1982         ca_event->ErrCode = UPNP_E_SUCCESS;
1983         snprintf(resultStr, RESULT_LEN, "<u:%sResponse xmlns:u=\"%s\">\n%s\n</u:%sResponse>",
1984                  ca_event->ActionName, WANIP_SERVICE_TYPE, "", ca_event->ActionName);
1985         ca_event->ActionResult = ixmlParseBuffer(resultStr);
1986     }
1987
1988     ixmlDocument_free(propSet);
1989     free(start_port);
1990     free(end_port);
1991     free(proto);
1992     free(bool_manage);
1993
1994     return(ca_event->ErrCode);
1995 }
1996
1997 /**
1998  * WANIPConnection:2 Action: GetListOfPortmappings
1999  * 
2000  * This action returns a list of port mappings matching the arguments. 
2001  * 
2002  * @param ca_event Upnp event struct.
2003  * @return Upnp error code.
2004  */
2005 int GetListOfPortmappings(struct Upnp_Action_Request *ca_event)
2006 {
2007     char *start_port = NULL;
2008     char *end_port = NULL;
2009     char *manage = NULL;
2010     char *proto = NULL;
2011     char *number_of_ports = NULL;
2012     char cp_ip[INET_ADDRSTRLEN] = "";
2013     char result_str[RESULT_LEN_LONG]; // TODO: dynamically allocated result_str
2014
2015     int start, end;
2016     int max_entries, chars_wrote;
2017     int action_succeeded = 0, action_fail_exit = 0;
2018     int result_place = 0;
2019     int authorized = 0;
2020     struct portMap *pm = NULL;
2021
2022     if ( (start_port = GetFirstDocumentItem(ca_event->ActionRequest, "NewStartPort") )
2023             && (end_port = GetFirstDocumentItem(ca_event->ActionRequest, "NewEndPort") )
2024             && (manage = GetFirstDocumentItem(ca_event->ActionRequest, "NewManage") )
2025             && (number_of_ports = GetFirstDocumentItem(ca_event->ActionRequest, "NewNumberOfPorts") )
2026             && (GetNbSoapParameters(ca_event->ActionRequest, "u:GetListOfPortmappings") == 5 )
2027             && (isStringInteger(start_port) )
2028             && (isStringInteger(end_port) )
2029             && (isStringInteger(number_of_ports) )
2030             && (proto = GetFirstDocumentItem(ca_event->ActionRequest, "NewProtocol") ) )
2031     {
2032         //check if authorized
2033         if (AuthorizeControlPoint(ca_event, 1, 0) == CONTROL_POINT_AUTHORIZED)
2034         {
2035             authorized = 1;
2036         }
2037         if (((strcmp(proto, "TCP") != 0) && (strcmp(proto, "UDP") != 0)) || 
2038             (atoi(start_port) < 0) ||
2039             (atoi(end_port) < 0) )
2040         {
2041             trace(1, "%s: Argument value out of range",ca_event->ActionName);
2042             addErrorData(ca_event, 601, "Argument Value Out of Range");
2043         }
2044         // check that port values are greater than or equal to 1024 if CP is not authorized
2045         else if (!authorized && (atoi(start_port) < 1024 || atoi(end_port) < 1024))
2046         {
2047             trace(1, "Failure in GetListOfPortmappings: StartPort:%s EndPort:%s Proto:%s Manage:%s. Port values under 1024 and CP is not authorized\n",start_port,end_port,proto,manage);
2048             addErrorData(ca_event, 606, "Action not authorized");
2049         }
2050         else if ((end = atoi(end_port)) < (start = atoi(start_port)))
2051         {
2052             trace(1, "Failure in GetListOfPortmappings: StartPort:%s EndPort:%s Proto:%s Manage:%s InconsistentParameters!\n", start_port,end_port,proto,manage);
2053             addErrorData(ca_event, 733, "InconsistentParameters");
2054         }
2055         else
2056         {
2057             max_entries = atoi(number_of_ports);
2058             if (max_entries == 0)
2059                 max_entries = INT_MAX;
2060
2061             // If manage is not true or CP is not authorized, list only CP's port mappings
2062             if ( !resolveBoolean(manage) || !authorized )
2063                 inet_ntop(AF_INET, &ca_event->CtrlPtIPAddr, cp_ip, INET_ADDRSTRLEN);
2064
2065             // Write XML header
2066             result_place += snprintf(result_str, RESULT_LEN_LONG, xml_portmapListingHeader);
2067             if (result_place > RESULT_LEN_LONG)
2068             {
2069                 // if buffer runs out of space, return error
2070                 action_fail_exit = 1;
2071             }
2072
2073             // Loop through port mappings until we run out or max_entries reaches 0
2074             while (!action_fail_exit && (pm = pmlist_FindRangeAfter(start, end, proto, cp_ip, pm)) != NULL && max_entries--)
2075             {
2076                 chars_wrote = snprintf(&result_str[result_place], RESULT_LEN_LONG-result_place, xml_portmapEntry,
2077                                        pm->m_RemoteHost, pm->m_ExternalPort, pm->m_PortMappingProtocol,
2078                                        pm->m_InternalPort, pm->m_InternalClient, pm->m_PortMappingEnabled,
2079                                        pm->m_PortMappingDescription, (pm->expirationTime-time(NULL)));
2080
2081                 // if buffer runs out of space, return error
2082                 if (chars_wrote > RESULT_LEN_LONG-result_place)
2083                 {
2084                     action_succeeded = 0;
2085                     action_fail_exit = 1;
2086                     break;
2087                 }
2088
2089                 result_place += chars_wrote;
2090                 action_succeeded = 1;
2091             }
2092
2093             if (action_succeeded)
2094             {
2095                 chars_wrote = snprintf(&result_str[result_place], RESULT_LEN_LONG-result_place, xml_portmapListingFooter);
2096                 if (chars_wrote > RESULT_LEN_LONG-result_place)
2097                 {
2098                     // if buffer runs out of space, return error
2099                     trace(2, "GetListOfPortmappings: Failure while creating result string");
2100                     addErrorData(ca_event, 501, "Action Failed");
2101                 }
2102                 else
2103                 {
2104                     // this will automatically escape value of NewPortListing
2105                     ca_event->ActionResult = UpnpMakeActionResponse(ca_event->ActionName, WANIP_SERVICE_TYPE,
2106                                               1, 
2107                                               "NewPortListing", result_str,
2108                                               NULL);
2109                     ca_event->ErrCode = UPNP_E_SUCCESS;
2110                     trace(3, "[This is un-escaped value of response]\n%s",result_str);
2111                 }
2112             }
2113             else if (action_fail_exit)
2114             {
2115                 trace(2, "GetListOfPortmappings: Failure while creating result string");
2116                 addErrorData(ca_event, 501, "Action Failed");
2117             }
2118             else
2119             {
2120                 trace(2, "GetListOfPortmappings: Portmapping does not exist");
2121                 addErrorData(ca_event, 730, "PortMappingNotFound");
2122             }
2123         }
2124     }
2125     else
2126     {
2127         trace(1, "GetListOfPortmappings: Invalid Arguments\n\tStartPort: %s EndPort: %s Proto: %s NumberOfPorts: %s Manage: %s",
2128               start_port, end_port, proto, number_of_ports, manage);
2129         addErrorData(ca_event, 402, "Invalid Args");
2130     }
2131
2132     free(start_port);
2133     free(end_port);
2134     free(proto);
2135     free(manage);
2136     free(number_of_ports);
2137
2138     return ca_event->ErrCode;
2139 }
2140
2141
2142 //-----------------------------------------------------------------------------
2143 //
2144 //                      WANEthernetLinkConfig:1 Service Actions
2145 //
2146 //-----------------------------------------------------------------------------
2147
2148 /**
2149  * WANEthernetLinkConfig:1 Action: GetEthernetLinkStatus
2150  * 
2151  * This action retrieves the link status of the external Ethernet connection.
2152  * 
2153  * @param ca_event Upnp event struct.
2154  * @return Upnp error code.
2155  */
2156 int GetEthernetLinkStatus (struct Upnp_Action_Request *ca_event)
2157 {
2158     char resultStr[RESULT_LEN];
2159     IXML_Document *result;
2160
2161     if(GetNbSoapParameters(ca_event->ActionRequest, "u:GetEthernetLinkStatus") != 0)
2162     {
2163         trace(1, "GetEthernetLinkStatus invalid args");
2164         addErrorData(ca_event, UPNP_SOAP_E_INVALID_ARGS, "Invalid Args");
2165         return ca_event->ErrCode; 
2166     } 
2167
2168     setEthernetLinkStatus(EthernetLinkStatus, g_vars.extInterfaceName);
2169
2170     snprintf(resultStr, RESULT_LEN,
2171              "<u:GetEthernetLinkStatusResponse xmlns:u=\"urn:schemas-upnp-org:service:WANEthernetLinkConfig:1\">\n"
2172              "<NewEthernetLinkStatus>%s</NewEthernetLinkStatus>\n"
2173              "</u:GetEthernetLinkStatusResponse>",EthernetLinkStatus);
2174
2175     // Create a IXML_Document from resultStr and return with ca_event
2176     if ((result = ixmlParseBuffer(resultStr)) != NULL)
2177     {
2178         ca_event->ActionResult = result;
2179         ca_event->ErrCode = UPNP_E_SUCCESS;
2180     }
2181     else
2182     {
2183         trace(1, "Error parsing Response to GetEthernetLinkStatus: %s", resultStr);
2184         ca_event->ActionResult = NULL;
2185         ca_event->ErrCode = 501;
2186     }
2187
2188     return(ca_event->ErrCode);
2189 }
2190
2191
2192 //-----------------------------------------------------------------------------
2193 //
2194 //                      Internal functionalities
2195 //
2196 //-----------------------------------------------------------------------------
2197
2198 /**
2199  * Initialize expiration timer thread, which maintains expirations of portmappings.
2200  * 
2201  * @return Upnp error code.
2202  */
2203 int ExpirationTimerThreadInit(void)
2204 {
2205     int retVal;
2206     ThreadPoolAttr attr;
2207     TPAttrInit( &attr );
2208     TPAttrSetMaxThreads( &attr, MAX_THREADS );
2209     TPAttrSetMinThreads( &attr, MIN_THREADS );
2210     TPAttrSetStackSize( &attr, ITHREAD_STACK_MIN ); 
2211     TPAttrSetJobsPerThread( &attr, JOBS_PER_THREAD );
2212     TPAttrSetIdleTime( &attr, THREAD_IDLE_TIME );
2213
2214     if ( ThreadPoolInit( &gExpirationThreadPool, &attr ) != UPNP_E_SUCCESS )
2215     {
2216         return UPNP_E_INIT_FAILED;
2217     }
2218
2219     if ( ( retVal = TimerThreadInit( &gExpirationTimerThread,
2220                                      &gExpirationThreadPool ) ) !=
2221             UPNP_E_SUCCESS )
2222     {
2223         return retVal;
2224     }
2225
2226     createEventUpdateTimer();
2227
2228     return 0;
2229 }
2230
2231 /**
2232  * Quit expiration timer thread.
2233  * 
2234  * @return Upnp error code.
2235  */
2236 int ExpirationTimerThreadShutdown(void)
2237 {
2238     TimerThreadShutdown(&gExpirationTimerThread);
2239     return ThreadPoolShutdown(&gExpirationThreadPool);
2240 }
2241
2242 /**
2243  * Set expiration event free.
2244  * 
2245  * @param event Expiration event.
2246  */
2247 void free_expiration_event(expiration_event *event)
2248 {
2249     if (event!=NULL && event->mapping!=NULL)
2250         event->mapping->expirationEventId = -1;
2251     free(event);
2252 }
2253
2254 /**
2255  * Create timer for disconnecting WAN connection automatically after time defined by 
2256  * AutoDisconnectTime state variable has elapsed from the connection status changing
2257  * to Connected status.
2258  * 
2259  * Every time the state of connection changes to "Connected", this timer must be updated!
2260  * See ConnectionStatusEventing().
2261  * 
2262  * @return Upnp error code.
2263  */
2264 int createAutoDisconnectTimer(void)
2265 {
2266     int result = 0;
2267     // cancel possible previous autodisconnect job
2268     if (gAutoDisconnectJobId != -1)
2269     {
2270         trace(3,"Remove previous AutoDisconnect timer");
2271         TimerThreadRemove(&gExpirationTimerThread, gAutoDisconnectJobId, NULL);
2272     }
2273
2274     if (AutoDisconnectTime > 0)
2275     {
2276         trace(3,"Create new AutoDisconnect timer to be executed after %ld seconds",AutoDisconnectTime);
2277         long int *pointer_to_delay = (long int *)malloc(sizeof(long int));
2278         *pointer_to_delay = WarnDisconnectDelay;
2279         // schedule new autodisconnect job
2280         ThreadPoolJob job;
2281         // Add disconnect job
2282         TPJobInit( &job, ( start_routine ) DisconnectWAN, pointer_to_delay );
2283         result = TimerThreadSchedule( &gExpirationTimerThread,
2284                                         AutoDisconnectTime,
2285                                         REL_SEC, &job, SHORT_TERM,
2286                                         &gAutoDisconnectJobId );
2287     }
2288     return result;
2289 }
2290
2291 /**
2292  * Disconnects WAN connection. Does the needed eventing.
2293  * 
2294  * @param input Pointer to long int which contains possible WarnDisconnectDelay. Must be pointer that is possible to free. Or NULL.
2295  */
2296 void DisconnectWAN(void *input)
2297 {
2298     IXML_Document *propSet = NULL;
2299     long int *delay = (long int *)input;
2300     trace(2, "Request for WAN connection termination received. Start disconnecting... ");
2301     if (strcmp(ConnectionStatus, "Disconnected") == 0 || strcmp(ConnectionStatus, "Disconnecting") == 0)
2302     {
2303         trace(2, "Disconnecting has been already started from somewhere else. Cancel this one. Status of connection: %s",ConnectionStatus);
2304         free(input);
2305         return;
2306     }
2307
2308     //TODO: Some really good way to get serviceids and UDNs from descdoc!!
2309     if (input && *delay > 0)
2310     {
2311         trace(3, "Pending WAN connection termination for %ld seconds...", *delay);
2312         strcpy(ConnectionStatus, "PendingDisconnect");
2313         UpnpAddToPropertySet(&propSet, "ConnectionStatus", ConnectionStatus);
2314         NotifyExtForIPv4AndIPv6(wanConnectionUDN, "urn:upnp-org:serviceId:WANIPConn1", propSet);
2315         ixmlDocument_free(propSet);
2316         propSet = NULL;
2317
2318         /* Sleep one second at a time and check if connection status has changed to Connected.
2319          * This way we don't block the next possible disconnect job initiated from DisconnectWAN()
2320          */
2321         long int slept;
2322         for (slept = 0; slept <= *delay; slept++)
2323         {
2324             if (strcmp(ConnectionStatus, "Connected") == 0)
2325             {
2326                 /* so somebody called RequestConnection while we were sleeping. Don't terminate then. */
2327                 trace(3, "WAN connection termination has been canceled.");
2328                 free(input);
2329                 return;
2330             }
2331             sleep(1);
2332         }
2333     }
2334
2335     strcpy(ConnectionStatus, "Disconnecting");
2336     UpnpAddToPropertySet(&propSet, "ConnectionStatus", ConnectionStatus);
2337     NotifyExtForIPv4AndIPv6(wanConnectionUDN, "urn:upnp-org:serviceId:WANIPConn1", propSet);
2338     ixmlDocument_free(propSet);
2339     propSet = NULL;
2340
2341     // terminate
2342     if (releaseIP(g_vars.extInterfaceName))
2343     {
2344         trace(3, "WAN connection Disconnected!");
2345     }
2346     else
2347     {
2348         trace(3, "Failed to disconnect WAN connection!");
2349     }
2350
2351     GetConnectionStatus(ConnectionStatus, g_vars.extInterfaceName);
2352     // Event ConnectionStatus
2353     UpnpAddToPropertySet(&propSet, "ConnectionStatus", ConnectionStatus);
2354     NotifyExtForIPv4AndIPv6(wanConnectionUDN, "urn:upnp-org:serviceId:WANIPConn1", propSet);
2355     ixmlDocument_free(propSet);
2356
2357     if (strcmp(ConnectionStatus, "Disconnected") == 0)
2358     {
2359         trace(2, "Disconnecting WAN connection succeeded. State of connection: '%s'",ConnectionStatus);
2360     }
2361     else
2362     {
2363         trace(2, "Disconnecting WAN connection failed. State of connection: '%s'",ConnectionStatus);
2364     }
2365     free(input);
2366 }
2367
2368 /**
2369  * This timer is used to check periodically if values of some state variables have changed.
2370  * If some has chandeg then event is sent to control points which has subscribed those events.
2371  * 
2372  * @return Upnp error code.
2373  */
2374 int createEventUpdateTimer(void)
2375 {
2376     expiration_event *event;
2377     event = ( expiration_event * ) malloc( sizeof( expiration_event ) );
2378     if ( event == NULL )
2379     {
2380         return 0;
2381     }
2382     event->mapping=NULL;
2383
2384     // Add event update job
2385     TPJobInit( &gEventUpdateJob, ( start_routine ) UpdateEvents, event );
2386     TPJobSetFreeFunction( &gEventUpdateJob, ( free_routine ) free_expiration_event );
2387     TimerThreadSchedule( &gExpirationTimerThread,
2388                          g_vars.eventUpdateInterval,
2389                          REL_SEC, &gEventUpdateJob, SHORT_TERM,
2390                          &( event->eventId ) );
2391     return  event->eventId;
2392 }
2393
2394 /**
2395  * Updates global variable idle_time which tells how long the WAN connection has been unused.
2396  * If idle_time is equal or greater than IdleDisconnectTime, then the WAN connection is terminated.
2397  */
2398 static void updateIdleTime()
2399 {
2400     if (IdleDisconnectTime <= 0 || strcmp(ConnectionStatus, "Connected") != 0)
2401         return;
2402
2403     unsigned long stats[STATS_LIMIT];
2404     if (!readStats(stats))
2405     {
2406         return;
2407     }
2408
2409     if (stats[STATS_RX_PACKETS] != connection_stats[STATS_RX_PACKETS] ||
2410          stats[STATS_TX_PACKETS] != connection_stats[STATS_TX_PACKETS])
2411         idle_time = 0;
2412     else 
2413         idle_time += g_vars.eventUpdateInterval; // this function is executed every g_vars.eventUpdateInterval seconds
2414
2415     // if we have idled long enough lets terminate WAN connection
2416     if (idle_time >= IdleDisconnectTime)
2417     {
2418         trace(2,"WAN connection has been idling for IdleDisconnectTime %ld seconds. Terminate connection.",IdleDisconnectTime);
2419         long int *pointer_to_delay = (long int *)malloc(sizeof(long int));
2420         *pointer_to_delay = WarnDisconnectDelay;
2421         DisconnectWAN((void *)pointer_to_delay);
2422         idle_time = 0;
2423     }
2424     else
2425     {
2426         connection_stats[STATS_RX_BYTES] = stats[STATS_RX_BYTES];
2427         connection_stats[STATS_RX_PACKETS] = stats[STATS_RX_PACKETS];
2428         connection_stats[STATS_TX_BYTES] = stats[STATS_TX_BYTES];
2429         connection_stats[STATS_TX_PACKETS] = stats[STATS_TX_PACKETS];
2430     }
2431 }
2432
2433 /**
2434  * UpdateEventTimer calls this to check if state variables, which may change on they own,
2435  * have changed. These variables are EthernetLinkStatus, ExternalIPAddress and ConnectionStatus.
2436  * 
2437  * @param input This parameter not used.
2438  */
2439 void UpdateEvents(void *input)
2440 {
2441     IXML_Document *propSet = NULL;
2442     expiration_event *event = ( expiration_event * ) input;
2443
2444     ithread_mutex_lock(&DevMutex);
2445
2446     EthernetLinkStatusEventing(propSet);
2447     ExternalIPAddressEventing(propSet);
2448     ConnectionStatusEventing(propSet);
2449     WANIPv6FirewallStatusEventing(propSet);
2450
2451     // this is not anything to do with eventing, but because this function is regularly executed this is here also.
2452     updateIdleTime();
2453
2454     ithread_mutex_unlock(&DevMutex);
2455
2456     ixmlDocument_free(propSet);
2457
2458     free_expiration_event(event);
2459
2460     // create update event again
2461     createEventUpdateTimer();
2462 }
2463
2464 /**
2465  * Check if EthernetLinkStatus state variable has changed since last check.
2466  * Update value and send notification for control points if it has changed.
2467  * 
2468  * @param propSet IXML_Document used for notification.
2469  * @return 1 if EthernetLinkStatus has changed, 0 if not.
2470  */
2471 int EthernetLinkStatusEventing(IXML_Document *propSet)
2472 {
2473     char prevStatus[12];
2474
2475     strcpy(prevStatus,EthernetLinkStatus);
2476     setEthernetLinkStatus(EthernetLinkStatus, g_vars.extInterfaceName);
2477
2478     // has status changed?
2479     if (strcmp(prevStatus,EthernetLinkStatus) != 0)
2480     {
2481         UpnpAddToPropertySet(&propSet, "EthernetLinkStatus", EthernetLinkStatus);
2482         NotifyExtForIPv4AndIPv6(wanConnectionUDN, "urn:upnp-org:serviceId:WANEthLinkC1", propSet);
2483
2484         trace(2, "EthernetLinkStatus changed: From %s to %s",prevStatus,EthernetLinkStatus);
2485         ixmlDocument_free(propSet);
2486         propSet = NULL;
2487         return 1;
2488     }
2489     return 0;
2490 }
2491
2492 /**
2493  * Check if ExternalIPAddress state variable has changed since last check.
2494  * Update value and send notification for control points if it has changed.
2495  * 
2496  * @param propSet IXML_Document used for notification.
2497  * @return 1 if ExternalIPAddress has changed, 0 if not.
2498  */
2499 int ExternalIPAddressEventing(IXML_Document *propSet)
2500 {
2501     char prevStatus[INET6_ADDRSTRLEN];
2502
2503     strcpy(prevStatus,ExternalIPAddress);
2504     GetIpAddressStr(ExternalIPAddress, g_vars.extInterfaceName);
2505
2506     // has status changed?
2507     if (strcmp(prevStatus,ExternalIPAddress) != 0)
2508     {
2509         UpnpAddToPropertySet(&propSet, "ExternalIPAddress", ExternalIPAddress);
2510         NotifyExtForIPv4AndIPv6(wanConnectionUDN, "urn:upnp-org:serviceId:WANIPConn1", propSet);
2511         trace(2, "ExternalIPAddress changed: From %s to %s",prevStatus,ExternalIPAddress);
2512         ixmlDocument_free(propSet);
2513         propSet = NULL;
2514         return 1;
2515     }
2516     return 0;
2517 }
2518
2519 /**
2520  * Check if ConnectionStatus state variable has changed since last check.
2521  * Update value and send notification for control points if it has changed.
2522  * 
2523  * If state has changed and the new state of connection is "Connected", reset
2524  * AutoDisconnectTimer.
2525  * 
2526  * @param propSet IXML_Document used for notification.
2527  * @return 1 if ConnectionStatus has changed, 0 if not.
2528  */
2529 int ConnectionStatusEventing(IXML_Document *propSet)
2530 {
2531     /* this is done only if previous state of connection is either "Connected" or "Disconnected"
2532      * Other states are caused by DisconnectWAN or RequestConnection and they will take care of 
2533      * eventing of those states. 
2534      */
2535     if (strcmp(ConnectionStatus, "Connected") == 0 || strcmp(ConnectionStatus, "Disconnected") == 0)
2536     {
2537         char prevStatus[20];
2538
2539         strcpy(prevStatus,ConnectionStatus);
2540         GetConnectionStatus(ConnectionStatus, g_vars.extInterfaceName);
2541
2542         // has status changed?
2543         if (strcmp(prevStatus,ConnectionStatus) != 0)
2544         {
2545             // if new status is connected, we create autodisconnecttimer and set startup time for Uptime statevariable
2546             if (strcmp(ConnectionStatus, "Connected") == 0)
2547             {
2548                 createAutoDisconnectTimer();
2549                 // Record the startup time, for uptime
2550                 startup_time = time(NULL);
2551             }
2552
2553             UpnpAddToPropertySet(&propSet, "ConnectionStatus", ConnectionStatus);
2554             NotifyExtForIPv4AndIPv6(wanConnectionUDN, "urn:upnp-org:serviceId:WANIPConn1", propSet);
2555             trace(2, "ConnectionStatus changed: From %s to %s",prevStatus,ConnectionStatus);
2556             ixmlDocument_free(propSet);
2557             propSet = NULL;
2558             return 1;
2559         }
2560     }
2561     return 0;
2562 }
2563
2564 /**
2565  * Check the state variables of the WANIPv6FirewallControl service
2566  * Those variables are only changed in the /etc/upnpd.conf file
2567  * A web service should be developped for that
2568  * The only purpose of thisfunction is to test the GENA events
2569  */
2570 int WANIPv6FirewallStatusEventing(IXML_Document *propSet)
2571 {
2572     int ipv6firewall_enabled = g_vars.ipv6firewallEnabled;
2573     int ipv6inbound_pinhole_allowed = g_vars.ipv6inboundPinholeAllowed;
2574
2575     char FirewallEnabled[2] = {'\0'};
2576     char InboundPinholeAllowed[2] = {'\0'};
2577
2578     if(parseConfigFile(&g_vars))
2579     {
2580         perror("Error parsing config file");
2581         return 0;
2582     }
2583     // has status changed?
2584     if (g_vars.ipv6firewallEnabled != ipv6firewall_enabled
2585             || g_vars.ipv6inboundPinholeAllowed != ipv6inbound_pinhole_allowed)
2586     {
2587         if(g_vars.ipv6firewallEnabled != ipv6firewall_enabled)
2588         {