source: trunk/libtransmission/upnp.c @ 2544

Last change on this file since 2544 was 2544, checked in by charles, 15 years ago

this looks bug but it's not: just janitorial cleanup, moving #includes from headers into source file

  • Property svn:keywords set to Date Rev Author Id
File size: 42.9 KB
Line 
1/******************************************************************************
2 * $Id: upnp.c 2544 2007-07-29 18:11:21Z charles $
3 *
4 * Copyright (c) 2006-2007 Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#include <assert.h>
26#include <errno.h>
27#include <limits.h>
28#include <stdarg.h>
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#include <time.h>
33
34#include <sys/types.h>
35#include <sys/socket.h>
36#include <netinet/in.h>
37#include <arpa/inet.h>
38
39#include "transmission.h"
40#include "http.h"
41#include "net.h"
42#include "trcompat.h"
43#include "upnp.h"
44#include "xml.h"
45
46/* uncomment this to log requests and responses to ~/transmission-upnp.log */
47/* #define VERBOSE_LOG */
48
49#define SSDP_ADDR               "239.255.255.250"
50#define SSDP_PORT               1900
51#define SSDP_TYPE               "upnp:rootdevice"
52#define SSDP_SUBTYPE            "ssdp:alive"
53#define SSDP_FIRST_DELAY        3750    /* 3 3/4 seconds */
54#define SSDP_MAX_DELAY          1800000 /* 30 minutes */
55#define SERVICE_TYPE_IP         "urn:schemas-upnp-org:service:WANIPConnection:1"
56#define SERVICE_TYPE_PPP        "urn:schemas-upnp-org:service:WANPPPConnection:1"
57#define SOAP_ENVELOPE           "http://schemas.xmlsoap.org/soap/envelope/"
58#define LOOP_DETECT_THRESHOLD   10 /* error on 10 add/get/del state changes */
59#define MAPPING_CHECK_INTERVAL  900000 /* 15 minutes */
60#define HTTP_REQUEST_INTERVAL   500 /* half a second */
61#define SOAP_METHOD_NOT_ALLOWED 405
62#define IGD_GENERIC_ERROR       500
63#define IGD_GENERIC_FAILED      501
64#define IGD_NO_MAPPING_EXISTS   714
65#define IGD_ADD_CONFLICT        718
66#define IGD_NO_DYNAMIC_MAPPING  725
67
68typedef struct tr_upnp_action_s
69{
70    char * name;
71    int    len;
72    struct { char * name; char * var; char dir; } * args;
73} tr_upnp_action_t;
74
75typedef struct tr_upnp_device_s
76{
77    char                    * id;
78    char                    * host;
79    char                    * root;
80    int                       port;
81    int                       ppp;
82    char                    * soap;
83    char                    * scpd;
84    int                       mappedport;
85    char                    * myaddr;
86#define UPNPDEV_STATE_ROOT              1
87#define UPNPDEV_STATE_SCPD              2
88#define UPNPDEV_STATE_READY             3
89#define UPNPDEV_STATE_ADD               4
90#define UPNPDEV_STATE_GET               5
91#define UPNPDEV_STATE_DEL               6
92#define UPNPDEV_STATE_MAPPED            7
93#define UPNPDEV_STATE_ERROR             8
94    uint8_t                   state;
95    uint8_t                   looping;
96    uint64_t                  lastrequest;
97    uint64_t                  lastcheck;
98    unsigned int              soapretry : 1;
99    tr_http_t               * http;
100    tr_upnp_action_t          getcmd;
101    tr_upnp_action_t          addcmd;
102    tr_upnp_action_t          delcmd;
103    struct tr_upnp_device_s * next;
104} tr_upnp_device_t;
105
106struct tr_upnp_s
107{
108    int                port;
109    int                infd;
110    int                outfd;
111    uint64_t           lastdiscover;
112    uint64_t           lastdelay;
113    unsigned int       active : 1;
114    unsigned int       discovering : 1;
115    tr_upnp_device_t * devices;
116};
117
118static int
119sendSSDP( int fd );
120static int
121mcastStart();
122static void
123killSock( int * sock );
124static void
125killHttp( tr_http_t ** http );
126static int
127watchSSDP( tr_upnp_device_t ** devices, int fd );
128static tr_tristate_t
129recvSSDP( int fd, char * buf, int * len );
130static int
131parseSSDP( char * buf, int len, tr_http_header_t * headers );
132static void
133deviceAdd( tr_upnp_device_t ** first, const char * id, int idLen,
134           const char * url, int urlLen );
135static void
136deviceRemove( tr_upnp_device_t ** prevptr );
137static int
138deviceStop( tr_upnp_device_t * dev );
139static int
140devicePulse( tr_upnp_device_t * dev, int port );
141static int
142devicePulseHttp( tr_upnp_device_t * dev,
143                 const char ** body, int * len );
144static tr_http_t *
145devicePulseGetHttp( tr_upnp_device_t * dev );
146static int
147parseRoot( const char * root, const char *buf, int len,
148           char ** soap, char ** scpd, int * ppp );
149static void
150addUrlbase( const char * base, char ** path );
151static int
152parseScpd( const char *buf, int len, tr_upnp_action_t * getcmd,
153           tr_upnp_action_t * addcmd, tr_upnp_action_t * delcmd );
154static int
155parseScpdArgs( const char * buf, const char * end,
156               tr_upnp_action_t * action, char dir );
157static int
158parseMapping( tr_upnp_device_t * dev, const char * buf, int len );
159static char *
160joinstrs( const char *, const char *, const char * );
161static tr_http_t *
162soapRequest( int retry, const char * host, int port, const char * path,
163             const char * type, tr_upnp_action_t * action, ... );
164static void
165actionSetup( tr_upnp_action_t * action, const char * name, int prealloc );
166static void
167actionFree( tr_upnp_action_t * action );
168static int
169actionAdd( tr_upnp_action_t * action, char * name, char * var,
170                      char dir );
171#define actionLookupVar( act, nam, len, dir ) \
172    ( actionLookup( (act), (nam), (len), (dir), 0 ) )
173#define actionLookupName( act, var, len, dir ) \
174    ( actionLookup( (act), (var), (len), (dir), 1 ) )
175static const char *
176actionLookup( tr_upnp_action_t * action, const char * key, int len,
177              char dir, int getname );
178
179#ifdef VERBOSE_LOG
180static FILE * vlog = NULL;
181#endif
182
183tr_upnp_t *
184tr_upnpInit()
185{
186    tr_upnp_t * upnp;
187
188    upnp = calloc( 1, sizeof( *upnp ) );
189    if( NULL == upnp )
190    {
191        return NULL;
192    }
193
194    upnp->infd     = -1;
195    upnp->outfd    = -1;
196
197#ifdef VERBOSE_LOG
198    if( NULL == vlog )
199    {
200        char path[MAX_PATH_LENGTH];
201        time_t stupid_api;
202        snprintf( path, sizeof path, "%s/transmission-upnp.log",
203                  tr_getHomeDirectory());
204        vlog = fopen( path, "a" );
205        stupid_api = time( NULL );
206        fprintf( vlog, "opened log at %s\n\n", ctime( &stupid_api ) );
207    }
208#endif
209
210    return upnp;
211}
212
213void
214tr_upnpStart( tr_upnp_t * upnp )
215{
216    if( !upnp->active )
217    {
218        tr_inf( "starting upnp" );
219        upnp->active = 1;
220        upnp->discovering = 1;
221        upnp->infd = mcastStart();
222        upnp->lastdiscover = 0;
223        upnp->lastdelay = SSDP_FIRST_DELAY / 2;
224    }
225}
226
227void
228tr_upnpStop( tr_upnp_t * upnp )
229{
230    if( upnp->active )
231    {
232        tr_inf( "stopping upnp" );
233        upnp->active = 0;
234        killSock( &upnp->infd );
235        killSock( &upnp->outfd );
236    }
237}
238
239int
240tr_upnpStatus( tr_upnp_t * upnp )
241{
242    tr_upnp_device_t * ii;
243    int                ret;
244
245    if( !upnp->active )
246    {
247        ret = ( NULL == upnp->devices ?
248                TR_NAT_TRAVERSAL_DISABLED : TR_NAT_TRAVERSAL_UNMAPPING );
249    }
250    else if( NULL == upnp->devices )
251    {
252        ret = TR_NAT_TRAVERSAL_NOTFOUND;
253    }
254    else
255    {
256        ret = TR_NAT_TRAVERSAL_MAPPING;
257        for( ii = upnp->devices; NULL != ii; ii = ii->next )
258        {
259            if( UPNPDEV_STATE_ERROR == ii->state )
260            {
261                ret = TR_NAT_TRAVERSAL_ERROR;
262            }
263            else if( 0 < ii->mappedport )
264            {
265                ret = TR_NAT_TRAVERSAL_MAPPED;
266                break;
267            }
268        }
269    }
270
271    return ret;
272}
273
274void
275tr_upnpForwardPort( tr_upnp_t * upnp, int port )
276{
277    tr_dbg( "upnp port changed from %i to %i", upnp->port, port );
278    upnp->port = port;
279}
280
281void
282tr_upnpRemoveForwarding( tr_upnp_t * upnp )
283{
284    tr_dbg( "upnp port unset" );
285    upnp->port = 0;
286}
287
288void
289tr_upnpClose( tr_upnp_t * upnp )
290{
291    tr_upnpStop( upnp );
292
293    while( NULL != upnp->devices )
294    {
295        deviceRemove( &upnp->devices );
296    }
297
298    free( upnp );
299
300#ifdef VERBOSE_LOG
301    if( NULL != vlog )
302    {
303        fflush( vlog );
304    }
305#endif
306
307}
308
309void
310tr_upnpPulse( tr_upnp_t * upnp )
311{
312    tr_upnp_device_t ** ii;
313
314    if( upnp->active )
315    {
316        /* pulse on all known devices */
317        upnp->discovering = 1;
318        for( ii = &upnp->devices; NULL != *ii; ii = &(*ii)->next )
319        {
320            if( devicePulse( *ii, upnp->port ) )
321            {
322                upnp->discovering = 0;
323            }
324        }
325
326        /* send an SSDP discover message */
327        if( upnp->discovering &&
328            upnp->lastdelay + upnp->lastdiscover < tr_date() )
329        {
330            upnp->outfd = sendSSDP( upnp->outfd );
331            upnp->lastdiscover = tr_date();
332            upnp->lastdelay = MIN( upnp->lastdelay * 2, SSDP_MAX_DELAY );
333        }
334
335        /* try to receive SSDP messages */
336        watchSSDP( &upnp->devices, upnp->infd );
337        if( watchSSDP( &upnp->devices, upnp->outfd ) )
338        {
339            killSock( &upnp->outfd );
340        }
341    }
342    else
343    {
344        /* delete all mappings then delete devices */
345        ii = &upnp->devices;
346        while( NULL != *ii )
347        {
348            if( deviceStop( *ii ) )
349            {
350                deviceRemove( ii );
351            }
352            else
353            {
354                devicePulse( *ii, 0 );
355                ii = &(*ii)->next;
356            }
357        }
358    }
359}
360
361static int
362sendSSDP( int fd )
363{
364    char buf[102];
365    int  len;
366    struct sockaddr_in sin;
367
368    if( 0 > fd )
369    {
370        fd = tr_netBindUDP( 0 );
371        if( 0 > fd )
372        {
373            return -1;
374        }
375    }
376
377    tr_dbg( "sending upnp ssdp discover message" );
378
379    len = snprintf( buf, sizeof( buf ),
380                    "M-SEARCH * HTTP/1.1\r\n"
381                    "Host: %s:%i\r\n"
382                    "Man: \"ssdp:discover\"\r\n"
383                    "ST: %s\r\n"
384                    "MX: 3\r\n"
385                    "\r\n",
386                    SSDP_ADDR, SSDP_PORT, SSDP_TYPE );
387
388    /* if this assertion ever fails then just increase the size of buf */
389    assert( (int) sizeof( buf ) > len );
390
391    memset( &sin, 0, sizeof( sin ) );
392    sin.sin_family      = AF_INET;
393    sin.sin_addr.s_addr = inet_addr( SSDP_ADDR );
394    sin.sin_port        = htons( SSDP_PORT );
395
396#ifdef VERBOSE_LOG
397    fprintf( vlog, "send ssdp message, %i bytes:\n", len );
398    fwrite( buf, 1, len, vlog );
399    fputs( "\n\n", vlog );
400#endif
401
402    if( 0 > sendto( fd, buf, len, 0,
403                    (struct sockaddr*) &sin, sizeof( sin ) ) )
404    {
405        if( EAGAIN != errno )
406        {
407            tr_err( "Could not send SSDP discover message (%s)",
408                    strerror( errno ) );
409        }
410        killSock( &fd );
411        return -1;
412    }
413
414    return fd;
415}
416
417static int
418mcastStart()
419{
420    int fd;
421    struct in_addr addr;
422
423    addr.s_addr = inet_addr( SSDP_ADDR );
424    fd = tr_netMcastOpen( SSDP_PORT, &addr );
425    if( 0 > fd )
426    {
427        return -1;
428    }
429
430    return fd;
431}
432
433static void
434killSock( int * sock )
435{
436    if( 0 <= *sock )
437    {
438        tr_netClose( *sock );
439        *sock = -1;
440    }
441}
442
443static void
444killHttp( tr_http_t ** http )
445{
446    tr_httpClose( *http );
447    *http = NULL;
448}
449
450static int
451watchSSDP( tr_upnp_device_t ** devices, int fd )
452{
453    /* XXX what if we get a huge SSDP packet? */
454    char buf[512];
455    int len;
456    tr_http_header_t hdr[] = {
457        /* first one must be type and second must be subtype */
458        { NULL,            NULL, 0 },
459        { "NTS",           NULL, 0 },
460        /* XXX should probably look at this
461           { "Cache-control", NULL, 0 }, */
462        { "Location",      NULL, 0 },
463        { "USN",           NULL, 0 },
464        { NULL,            NULL, 0 }
465    };
466    enum { OFF_TYPE = 0, OFF_SUBTYPE, OFF_LOC, OFF_ID };
467    int ret;
468
469    if( 0 > fd )
470    {
471        return 0;
472    }
473
474    ret = 0;
475    for(;;)
476    {
477        len = sizeof( buf );
478        switch( recvSSDP( fd, buf, &len ) )
479        {
480            case TR_NET_WAIT:
481                return ret;
482            case TR_NET_ERROR:
483                return 1;
484            case TR_NET_OK:
485                ret = 1;
486                if( parseSSDP( buf, len, hdr ) &&
487                    NULL != hdr[OFF_LOC].data &&
488                    NULL != hdr[OFF_ID].data )
489                {
490                    deviceAdd( devices, hdr[OFF_ID].data, hdr[OFF_ID].len,
491                               hdr[OFF_LOC].data, hdr[OFF_LOC].len );
492                }
493        }
494    }
495}
496
497static tr_tristate_t
498recvSSDP( int fd, char * buf, int * len )
499{
500    if( 0 > fd )
501    {
502        return TR_NET_ERROR;
503    }
504
505    *len = tr_netRecv( fd, ( uint8_t * ) buf, *len );
506    if( TR_NET_BLOCK & *len )
507    {
508        return TR_NET_WAIT;
509    }
510    else if( TR_NET_CLOSE & *len )
511    {
512        tr_err( "Could not receive SSDP message (%s)", strerror( errno ) );
513        return TR_NET_ERROR;
514    }
515    else
516    {
517#ifdef VERBOSE_LOG
518        fprintf( vlog, "receive ssdp message, %i bytes:\n", *len );
519        fwrite( buf, 1, *len, vlog );
520        fputs( "\n\n", vlog );
521#endif
522        return TR_NET_OK;
523    }
524}
525
526static int
527parseSSDP( char * buf, int len, tr_http_header_t * hdr )
528{
529    char *method, *uri, *body;
530    int code;
531
532    body = NULL;
533    /* check for an HTTP NOTIFY request */
534    if( 0 <= tr_httpRequestType( buf, len, &method, &uri ) )
535    {
536        if( 0 == tr_strcasecmp( method, "NOTIFY" ) && 0 == strcmp( uri, "*" ) )
537        {
538            hdr[0].name = "NT";
539            body = tr_httpParse( buf, len, hdr );
540            if( NULL == hdr[1].name ||
541                0 != tr_strncasecmp( SSDP_SUBTYPE, hdr[1].data, hdr[1].len ) )
542            {
543                body = NULL;
544            }
545            else
546            {
547                tr_dbg( "found upnp ssdp notify request" );
548            }
549        }
550        free( method );
551        free( uri );
552    }
553    else
554    {
555        /* check for a response to our HTTP M-SEARCH request */
556        code = tr_httpResponseCode( buf, len );
557        if( TR_HTTP_STATUS_OK( code ) )
558        {
559            hdr[0].name = "ST";
560            body = tr_httpParse( buf, len, hdr );
561            if( NULL != body )
562            {
563                tr_dbg( "found upnp ssdp m-search response" );
564            }
565        }
566    }
567
568    /* did we find enough information to be useful? */
569    if( NULL != body )
570    {
571        /* the first header is the type */
572        if( NULL != hdr[0].data &&
573            0 == tr_strncasecmp( SSDP_TYPE, hdr[0].data, hdr[0].len ) )
574        {
575            return 1;
576        }
577    }
578
579    return 0;
580}
581
582static void
583deviceAdd( tr_upnp_device_t ** first, const char * id, int idLen,
584           const char * url, int urlLen )
585{
586    tr_upnp_device_t * ii;
587
588    for( ii = *first; NULL != ii; ii = ii->next )
589    {
590        if( 0 == tr_strncasecmp( ii->id, id, idLen ) )
591        {
592            /* this device may have gone away and came back, recheck it */
593            ii->lastcheck = 0;
594            return;
595        }
596    }
597
598    ii = malloc( sizeof( *ii ) );
599    if( NULL == ii )
600    {
601        return;
602    }
603    memset( ii, 0, sizeof( *ii ) );
604    if( tr_httpParseUrl( url, urlLen, &ii->host, &ii->port, &ii->root ) )
605    {
606        tr_err( "Invalid HTTP URL from UPnP" );
607        free( ii );
608        return;
609    }
610    ii->id = tr_strndup( id, idLen );
611    ii->state = UPNPDEV_STATE_ROOT;
612    actionSetup( &ii->getcmd, "GetSpecificPortMappingEntry", 8 );
613    actionSetup( &ii->addcmd, "AddPortMapping", 8 );
614    actionSetup( &ii->delcmd, "DeletePortMapping", 3 );
615    ii->next = *first;
616    *first = ii;
617
618    tr_inf( "new upnp device %s, port %i, path %s",
619            ii->host, ii->port, ii->root );
620}
621
622static void
623deviceRemove( tr_upnp_device_t ** prevptr )
624{
625    tr_upnp_device_t * dead;
626
627    dead = *prevptr;
628    *prevptr = dead->next;
629
630    tr_inf( "forgetting upnp device %s", dead->host );
631
632    free( dead->id );
633    free( dead->host );
634    free( dead->root );
635    free( dead->soap );
636    free( dead->scpd );
637    free( dead->myaddr );
638    if( NULL != dead->http )
639    {
640        killHttp( &dead->http );
641    }
642    actionFree( &dead->getcmd );
643    actionFree( &dead->addcmd );
644    actionFree( &dead->delcmd );
645    free( dead );
646}
647
648static int
649deviceStop( tr_upnp_device_t * dev )
650{
651    switch( dev->state )
652    {
653        case UPNPDEV_STATE_READY:
654        case UPNPDEV_STATE_ERROR:
655            return 1;
656        case UPNPDEV_STATE_MAPPED:
657            tr_dbg( "upnp device %s: stopping upnp, state mapped -> delete",
658                dev->host );
659            dev->state = UPNPDEV_STATE_DEL;
660            return 0;
661        default:
662            return 0;
663    }
664}
665
666static int
667devicePulse( tr_upnp_device_t * dev, int port )
668{
669    const char * body;
670    int          len, code;
671    uint8_t      laststate;
672
673    switch( dev->state )
674    {
675        case UPNPDEV_STATE_READY:
676            if( 0 < port )
677            {
678                tr_dbg( "upnp device %s: want mapping, state ready -> get",
679                        dev->host );
680                dev->mappedport = port;
681                dev->state = UPNPDEV_STATE_GET;
682                break;
683            }
684            return 1;
685        case UPNPDEV_STATE_MAPPED:
686            if( port != dev->mappedport )
687            {
688                tr_dbg( "upnp device %s: change mapping, "
689                        "state mapped -> delete", dev->host );
690                dev->state = UPNPDEV_STATE_DEL;
691                break;
692            }
693            if( tr_date() > dev->lastcheck + MAPPING_CHECK_INTERVAL )
694            {
695                tr_dbg( "upnp device %s: check mapping, "
696                        "state mapped -> get", dev->host );
697                dev->state = UPNPDEV_STATE_GET;
698            }
699            return 1;
700        case UPNPDEV_STATE_ERROR:
701            return 0;
702    }
703
704    /* gcc can be pretty annoying about it's warnings sometimes */
705    len = 0;
706    body = NULL;
707
708    code = devicePulseHttp( dev, &body, &len );
709    if( 0 > code )
710    {
711        return 1;
712    }
713
714    if( LOOP_DETECT_THRESHOLD <= dev->looping )
715    {
716        tr_dbg( "upnp device %s: loop detected, state %hhu -> error",
717                dev->host, dev->state );
718        dev->state = UPNPDEV_STATE_ERROR;
719        dev->looping = 0;
720        killHttp( &dev->http );
721        return 1;
722    }
723
724    laststate = dev->state;
725    dev->state = UPNPDEV_STATE_ERROR;
726    switch( laststate ) 
727    {
728        case UPNPDEV_STATE_ROOT:
729            if( !TR_HTTP_STATUS_OK( code ) )
730            {
731                tr_dbg( "upnp device %s: fetch root failed with http code %i",
732                        dev->host, code );
733            }
734            else if( parseRoot( dev->root, body, len,
735                                &dev->soap, &dev->scpd, &dev->ppp ) )
736            {
737                tr_dbg( "upnp device %s: parse root failed", dev->host );
738            }
739            else
740            {
741                tr_dbg( "upnp device %s: found scpd \"%s\" and soap \"%s\"",
742                        dev->root, dev->scpd, dev->soap );
743                tr_dbg( "upnp device %s: parsed root, state root -> scpd",
744                        dev->host );
745                dev->state = UPNPDEV_STATE_SCPD;
746            }
747            break;
748
749        case UPNPDEV_STATE_SCPD:
750            if( !TR_HTTP_STATUS_OK( code ) )
751            {
752                tr_dbg( "upnp device %s: fetch scpd failed with http code %i",
753                        dev->host, code );
754            }
755            else if( parseScpd( body, len, &dev->getcmd,
756                                &dev->addcmd, &dev->delcmd ) )
757            {
758                tr_dbg( "upnp device %s: parse scpd failed", dev->host );
759            }
760            else
761            {
762                tr_dbg( "upnp device %s: parsed scpd, state scpd -> ready",
763                        dev->host );
764                dev->state = UPNPDEV_STATE_READY;
765                dev->looping = 0;
766            }
767            break;
768
769        case UPNPDEV_STATE_ADD:
770            dev->looping++;
771            if( IGD_ADD_CONFLICT == code )
772            {
773                tr_dbg( "upnp device %s: add conflict, state add -> delete",
774                        dev->host );
775                dev->state = UPNPDEV_STATE_DEL;
776            }
777            else if( TR_HTTP_STATUS_OK( code ) ||
778                     IGD_GENERIC_ERROR == code || IGD_GENERIC_FAILED == code )
779            {
780                tr_dbg( "upnp device %s: add attempt, state add -> get",
781                        dev->host );
782                dev->state = UPNPDEV_STATE_GET;
783            }
784            else
785            {
786                tr_dbg( "upnp device %s: add failed with http code %i",
787                        dev->host, code );
788            }
789            break;
790
791        case UPNPDEV_STATE_GET:
792            dev->looping++;
793            if( TR_HTTP_STATUS_OK( code ) )
794            {
795                switch( parseMapping( dev, body, len ) )
796                {
797                    case -1:
798                        break;
799                    case 0:
800                        tr_dbg( "upnp device %s: invalid mapping, "
801                                "state get -> delete", dev->host );
802                        dev->state = UPNPDEV_STATE_DEL;
803                        break;
804                    case 1:
805                        tr_dbg( "upnp device %s: good mapping, "
806                                "state get -> mapped", dev->host );
807                        dev->state = UPNPDEV_STATE_MAPPED;
808                        dev->looping = 0;
809                        dev->lastcheck = tr_date();
810                        tr_inf( "upnp successful for port %i",
811                                dev->mappedport );
812                        break;
813                    default:
814                        assert( 0 );
815                        break;
816                }
817            }
818            else if( IGD_NO_MAPPING_EXISTS == code ||
819                     IGD_GENERIC_ERROR == code || IGD_GENERIC_FAILED == code )
820            {
821                tr_dbg( "upnp device %s: no mapping, state get -> add",
822                        dev->host );
823                dev->state = UPNPDEV_STATE_ADD;
824            }
825            else
826            {
827                tr_dbg( "upnp device %s: get failed with http code %i",
828                        dev->host, code );
829            }
830            break;
831
832        case UPNPDEV_STATE_DEL:
833            dev->looping++;
834            if( TR_HTTP_STATUS_OK( code ) || IGD_NO_MAPPING_EXISTS == code ||
835                IGD_GENERIC_ERROR == code || IGD_GENERIC_FAILED == code )
836            {
837                tr_dbg( "upnp device %s: deleted, state delete -> ready",
838                        dev->host );
839                dev->state = UPNPDEV_STATE_READY;
840                dev->looping = 0;
841            }
842            else
843            {
844                tr_dbg( "upnp device %s: del failed with http code %i",
845                        dev->host, code );
846            }
847            break;
848
849        default:
850            assert( 0 );
851            break;
852    }
853
854    dev->lastrequest = tr_date();
855    killHttp( &dev->http );
856
857    if( UPNPDEV_STATE_ERROR == dev->state )
858    {
859        tr_dbg( "upnp device %s: error, state %hhu -> error",
860                dev->host, laststate );
861        return 0;
862    }
863
864    return 1;
865}
866
867static tr_http_t *
868makeHttp( int method, const char * host, int port, const char * path )
869{
870    if( tr_httpIsUrl( path, -1 ) )
871    {
872        return tr_httpClientUrl( method, "%s", path );
873    }
874    else
875    {
876        return tr_httpClient( method, host, port, "%s", path );
877    }
878}
879
880static tr_http_t *
881devicePulseGetHttp( tr_upnp_device_t * dev )
882{
883    tr_http_t  * ret;
884    char         numstr[6];
885    const char * type;
886#ifdef VERBOSE_LOG
887    const char * body;
888    int          len;
889#endif
890
891    ret = NULL;
892    switch( dev->state ) 
893    {
894        case UPNPDEV_STATE_ROOT:
895            if( !dev->soapretry )
896            {
897                ret = makeHttp( TR_HTTP_GET, dev->host, dev->port, dev->root );
898            }
899            break;
900        case UPNPDEV_STATE_SCPD:
901            if( !dev->soapretry )
902            {
903                ret = makeHttp( TR_HTTP_GET, dev->host, dev->port, dev->scpd );
904            }
905            break;
906        case UPNPDEV_STATE_ADD:
907            if( NULL == dev->myaddr )
908            {
909                ret = NULL;
910                break;
911            }
912            snprintf( numstr, sizeof( numstr ), "%i", dev->mappedport );
913            type = ( dev->ppp ? SERVICE_TYPE_PPP : SERVICE_TYPE_IP );
914            ret = soapRequest( dev->soapretry, dev->host, dev->port, dev->soap,
915                               type, &dev->addcmd,
916                               "PortMappingEnabled", "1",
917                               "PortMappingLeaseDuration", "0",
918                               "RemoteHost", "",
919                               "ExternalPort", numstr,
920                               "InternalPort", numstr,
921                               "PortMappingProtocol", "TCP",
922                               "InternalClient", dev->myaddr,
923                               "PortMappingDescription", "Added by " TR_NAME,
924                               NULL );
925            break;
926        case UPNPDEV_STATE_GET:
927            snprintf( numstr, sizeof( numstr ), "%i", dev->mappedport );
928            type = ( dev->ppp ? SERVICE_TYPE_PPP : SERVICE_TYPE_IP );
929            ret = soapRequest( dev->soapretry, dev->host, dev->port, dev->soap,
930                               type, &dev->getcmd,
931                               "RemoteHost", "",
932                               "ExternalPort", numstr,
933                               "PortMappingProtocol", "TCP",
934                               NULL );
935            break;
936        case UPNPDEV_STATE_DEL:
937            snprintf( numstr, sizeof( numstr ), "%i", dev->mappedport );
938            type = ( dev->ppp ? SERVICE_TYPE_PPP : SERVICE_TYPE_IP );
939            ret = soapRequest( dev->soapretry, dev->host, dev->port, dev->soap,
940                               type, &dev->delcmd,
941                               "RemoteHost", "",
942                               "ExternalPort", numstr,
943                               "PortMappingProtocol", "TCP",
944                               NULL );
945            break;
946        default:
947            assert( 0 );
948            break;
949    }
950
951#ifdef VERBOSE_LOG
952    if( NULL != ret )
953    {
954        tr_httpGetHeaders( ret, &body, &len );
955        fprintf( vlog, "send http message, %i bytes (headers):\n", len );
956        fwrite( body, 1, len, vlog );
957        tr_httpGetBody( ret, &body, &len );
958        fprintf( vlog, "\n\nsend http message, %i bytes (body):\n", len );
959        fwrite( body, 1, len, vlog );
960        fputs( "\n\n", vlog );
961    }
962#endif
963
964    return ret;
965}
966
967static int
968devicePulseHttp( tr_upnp_device_t * dev,
969                 const char ** body, int * len )
970{
971    const char * headers;
972    int          hlen, code;
973
974    if( NULL == dev->http )
975    {
976        if( tr_date() < dev->lastrequest + HTTP_REQUEST_INTERVAL )
977        {
978            return -1;
979        }
980        dev->lastrequest = tr_date();
981        dev->http = devicePulseGetHttp( dev );
982        if( NULL == dev->http )
983        {
984            tr_dbg( "upnp device %s: http init failed, state %hhu -> error",
985                    dev->host, dev->state );
986            dev->state = UPNPDEV_STATE_ERROR;
987            dev->soapretry = 0;
988            return -1;
989        }
990    }
991
992    if( NULL == dev->myaddr )
993    {
994        dev->myaddr = tr_httpWhatsMyAddress( dev->http );
995    }
996
997    switch( tr_httpPulse( dev->http, &headers, &hlen ) )
998    {
999        case TR_NET_OK:
1000#ifdef VERBOSE_LOG
1001    fprintf( vlog, "receive http message, %i bytes:\n", hlen );
1002    fwrite( headers, 1, hlen, vlog );
1003    fputs( "\n\n", vlog );
1004#endif
1005            code = tr_httpResponseCode( headers, hlen );
1006            if( SOAP_METHOD_NOT_ALLOWED == code && !dev->soapretry )
1007            {
1008                dev->soapretry = 1;
1009                killHttp( &dev->http );
1010                break;
1011            }
1012            dev->soapretry = 0;
1013            *body = tr_httpParse( headers, hlen, NULL );
1014            *len = ( NULL == *body ? 0 : hlen - ( *body - headers ) );
1015            return code;
1016        case TR_NET_ERROR:
1017            killHttp( &dev->http );
1018            if( dev->soapretry )
1019            {
1020                tr_dbg( "upnp device %s: http pulse failed, state %hhu -> error",
1021                        dev->host, dev->state );
1022                dev->state = UPNPDEV_STATE_ERROR;
1023                dev->soapretry = 0;
1024            }
1025            else
1026            {
1027                dev->soapretry = 1;
1028            }
1029            break;
1030        case TR_NET_WAIT:
1031            break;
1032    }
1033
1034    return -1;
1035}
1036
1037static int
1038parseRoot( const char * root, const char *buf, int len,
1039           char ** soap, char ** scpd, int * ppp )
1040{
1041    const char * end, * ii, * jj, * kk, * urlbase;
1042    char       * basedup;
1043
1044    *soap = NULL;
1045    *scpd = NULL;
1046    end = buf + len;
1047
1048    buf = tr_xmlFindTagContents( buf, end, "root" );
1049    urlbase = tr_xmlFindTag( buf, end, "urlBase" );
1050    urlbase = tr_xmlTagContents( urlbase, end );
1051    buf = tr_xmlFindTagContents( buf, end, "device" );
1052    if( tr_xmlFindTagVerifyContents( buf, end, "deviceType",
1053            "urn:schemas-upnp-org:device:InternetGatewayDevice:1", 1 ) )
1054    {
1055        return 1;
1056    }
1057    buf = tr_xmlFindTag( buf, end, "deviceList" );
1058    ii = tr_xmlTagContents( buf, end );
1059    for( ; NULL != ii; ii = tr_xmlSkipTag( ii, end ) )
1060    {
1061        ii = tr_xmlFindTag( ii, end, "device" );
1062        buf = tr_xmlTagContents( ii, end );
1063        if( tr_xmlFindTagVerifyContents( buf, end, "deviceType",
1064                "urn:schemas-upnp-org:device:WANDevice:1", 1 ) )
1065        {
1066            continue;
1067        }
1068        buf = tr_xmlFindTag( buf, end, "deviceList" );
1069        jj = tr_xmlTagContents( buf, end );
1070        for( ; NULL != jj; jj = tr_xmlSkipTag( jj, end ) )
1071        {
1072            jj = tr_xmlFindTag( jj, end, "device" );
1073            buf = tr_xmlTagContents( jj, end );
1074            if( tr_xmlFindTagVerifyContents( buf, end, "deviceType",
1075                    "urn:schemas-upnp-org:device:WANConnectionDevice:1", 1 ) )
1076            {
1077                continue;
1078            }
1079            buf = tr_xmlFindTag( buf, end, "serviceList" );
1080            kk = tr_xmlTagContents( buf, end );
1081            for( ; NULL != kk; kk = tr_xmlSkipTag( kk, end ) )
1082            {
1083                kk = tr_xmlFindTag( kk, end, "service" );
1084                buf = tr_xmlTagContents( kk, end );
1085                if( !tr_xmlFindTagVerifyContents( buf, end, "serviceType",
1086                                                  SERVICE_TYPE_IP, 1 ) )
1087                {
1088                    *soap = tr_xmlDupTagContents( buf, end, "controlURL");
1089                    *scpd = tr_xmlDupTagContents( buf, end, "SCPDURL");
1090                    *ppp  = 0;
1091                    break;
1092                }
1093                /* XXX we should save all services of both types and
1094                   try adding mappings for each in turn */
1095                else if( !tr_xmlFindTagVerifyContents( buf, end, "serviceType",
1096                                                       SERVICE_TYPE_PPP, 1 ) )
1097                {
1098                    *soap = tr_xmlDupTagContents( buf, end, "controlURL");
1099                    *scpd = tr_xmlDupTagContents( buf, end, "SCPDURL");
1100                    *ppp  = 1;
1101                    break;
1102                }
1103            }
1104        }
1105    }
1106
1107    if( NULL == urlbase )
1108    {
1109        basedup = strrchr( root, '/' );
1110        assert( NULL != basedup );
1111        basedup = tr_strndup( root, basedup - root + 1 );
1112    }
1113    else
1114    {
1115        basedup = tr_xmlDupContents( urlbase, end );
1116    }
1117    addUrlbase( basedup, soap );
1118    addUrlbase( basedup, scpd );
1119    free( basedup );
1120
1121    if( NULL != *soap && NULL != *scpd )
1122    {
1123        return 0;
1124    }
1125
1126    return 1;
1127}
1128
1129static void
1130addUrlbase( const char * base, char ** path )
1131{
1132    const char * middle;
1133    int          len;
1134    char       * joined;
1135
1136    if( NULL == base || NULL == *path ||
1137        '/' == **path || tr_httpIsUrl( *path, -1 ) )
1138    {
1139        return;
1140    }
1141
1142    len = strlen( base );
1143    middle = ( 0 >= len || '/' != base[len-1] ? "/" : "" );
1144    joined = joinstrs( base, middle, *path );
1145    free( *path );
1146    *path = joined;
1147}
1148
1149static int
1150parseScpd( const char * buf, int len, tr_upnp_action_t * getcmd,
1151           tr_upnp_action_t * addcmd, tr_upnp_action_t * delcmd )
1152{
1153    const char * end, * next, * sub, * name;
1154
1155    end = buf + len;
1156    next = buf;
1157
1158    next = tr_xmlFindTagContents( next, end, "scpd" );
1159    next = tr_xmlFindTagContents( next, end, "actionList" );
1160
1161    while( NULL != next )
1162    {
1163        next = tr_xmlFindTag( next, end, "action" );
1164        sub = tr_xmlTagContents( next, end );
1165        name = tr_xmlFindTagContents( sub, end, "name" );
1166        sub = tr_xmlFindTagContents( sub, end, "argumentList" );
1167        if( !tr_xmlVerifyContents( name, end, getcmd->name, 1 ) )
1168        {
1169            if( parseScpdArgs( sub, end, getcmd, 'i' ) ||
1170                parseScpdArgs( sub, end, getcmd, 'o' ) )
1171            {
1172                return 1;
1173            }
1174        }
1175        else if( !tr_xmlVerifyContents( name, end, addcmd->name, 1 ) )
1176        {
1177            if( parseScpdArgs( sub, end, addcmd, 'i' ) )
1178            {
1179                return 1;
1180            }
1181        }
1182        else if( !tr_xmlVerifyContents( name, end, delcmd->name, 1 ) )
1183        {
1184            if( parseScpdArgs( sub, end, delcmd, 'i' ) )
1185            {
1186                return 1;
1187            }
1188        }
1189        next = tr_xmlSkipTag( next, end );
1190    }
1191
1192    return 0;
1193}
1194
1195static int
1196parseScpdArgs( const char * buf, const char * end,
1197               tr_upnp_action_t * action, char dir )
1198{
1199    const char * sub, * which;
1200    char       * name, * var;
1201
1202    assert( 'i' == dir || 'o' == dir );
1203    which = ( 'i' == dir ? "in" : "out" );
1204
1205    while( NULL != buf )
1206    {
1207        sub = tr_xmlFindTagContents( buf, end, "argument" );
1208        if( !tr_xmlFindTagVerifyContents( sub, end, "direction", which, 1 ) )
1209        {
1210            name = tr_xmlDupTagContents( sub, end, "name" );
1211            var = tr_xmlDupTagContents( sub, end, "relatedStateVariable" );
1212            if( NULL == name || NULL == var )
1213            {
1214                free( name );
1215                free( var );
1216            }
1217            else if( actionAdd( action, name, var, dir ) )
1218            {
1219                return 1;
1220            }
1221        }
1222        buf = tr_xmlSkipTag( buf, end );
1223    }
1224
1225    return 0;
1226}
1227
1228static int
1229parseMapping( tr_upnp_device_t * dev, const char * buf, int len )
1230{
1231    const char * end, * down, * next, * var;
1232    int          varlen, pret, cret, eret;
1233    char       * val;
1234
1235    assert( 0 < dev->mappedport );
1236
1237    if( NULL == dev->myaddr )
1238    {
1239        return -1;
1240    }
1241
1242    pret = -1;
1243    cret = -1;
1244    eret = -1;
1245
1246    end = buf + len;
1247    down = buf;
1248    down = tr_xmlFindTagContents( down, end, "Envelope" );
1249    down = tr_xmlFindTagContents( down, end, "Body" );
1250    down = tr_xmlFindTagContents( down, end,
1251                                  "GetSpecificPortMappingEntryResponse" );
1252
1253    next = down;
1254    while( NULL != next )
1255    {
1256        var = tr_xmlTagName( next, end, &varlen );
1257        var = actionLookupVar( &dev->getcmd, var, varlen, 'o' );
1258        if( NULL != var )
1259        {
1260            val = tr_xmlDupContents( tr_xmlTagContents( next, end ), end );
1261            if( 0 == tr_strcasecmp( "InternalPort", var ) )
1262            {
1263                pret = ( strtol( val, NULL, 10 ) == dev->mappedport ? 1 : 0 );
1264            }
1265            else if( 0 == tr_strcasecmp( "InternalClient", var ) )
1266            {
1267                cret = ( 0 == strcmp( dev->myaddr, val ) ? 1 : 0 );
1268            }
1269            else if( 0 == tr_strcasecmp( "PortMappingEnabled", var ) )
1270            {
1271                eret = ( 0 == strcmp( "1", val ) ? 1 : 0 );
1272            }
1273            free( val );
1274        }
1275        next = tr_xmlSkipTag( next, end );
1276    }
1277
1278    return MIN( MIN( pret, cret), eret );
1279}
1280
1281static char *
1282joinstrs( const char * first, const char * delim, const char * second )
1283{
1284    char * ret;
1285    int    len1, len2, len3;
1286
1287    len1 = strlen( first );
1288    len2 = strlen( delim );
1289    len3 = strlen( second );
1290    ret = malloc( len1 + len2 + len3 + 1 );
1291    if( NULL == ret )
1292    {
1293        return NULL;
1294    }
1295
1296    memcpy( ret, first, len1 );
1297    memcpy( ret + len1, delim, len2 );
1298    memcpy( ret + len1 + len2, second, len3 );
1299    ret[len1 + len2 + len3] = '\0';
1300
1301    return ret;
1302}
1303
1304static tr_http_t *
1305soapRequest( int retry, const char * host, int port, const char * path,
1306             const char * type, tr_upnp_action_t * action, ... )
1307{
1308    tr_http_t  * http;
1309    va_list      ap;
1310    const char * name, * value;
1311    int          method;
1312    char       * actstr;
1313
1314    method = ( retry ? TR_HTTP_M_POST : TR_HTTP_POST );
1315    http = makeHttp( method, host, port, path );
1316    if( NULL != http )
1317    {
1318        tr_httpAddHeader( http, "Content-type",
1319                          "text/xml; encoding=\"utf-8\"" );
1320        actstr = NULL;
1321        asprintf( &actstr, "\"%s#%s\"", type, action->name );
1322        tr_httpAddHeader( http, "SOAPAction", actstr );
1323        free( actstr );
1324        if( retry )
1325        {
1326            tr_httpAddHeader( http, "Man", "\"" SOAP_ENVELOPE "\"" );
1327        }
1328        tr_httpAddBody( http, 
1329"<?xml version=\"1.0\" encoding=\"utf-8\" ?>"
1330"<s:Envelope"
1331"    xmlns:s=\"" SOAP_ENVELOPE "\""
1332"    s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
1333"  <s:Body>"
1334"    <u:%s xmlns:u=\"%s\">", action->name, type );
1335
1336        va_start( ap, action );
1337        do
1338        {
1339            name = va_arg( ap, const char * );
1340            value = NULL;
1341            name = actionLookupName( action, name, -1, 'i' );
1342            if( NULL != name )
1343            {
1344                value = va_arg( ap, const char * );
1345                if( NULL != value )
1346                {
1347                    tr_httpAddBody( http,
1348"      <%s>%s</%s>", name, value, name );
1349                }
1350                else 
1351                {
1352                    tr_httpAddBody( http,
1353"      <%s></%s>", name, name );
1354                }
1355            }
1356        }
1357        while( NULL != name && NULL != value );
1358        va_end( ap );
1359
1360        tr_httpAddBody( http,
1361"    </u:%s>"
1362"  </s:Body>"
1363"</s:Envelope>", action->name );
1364    }
1365
1366    return http;
1367}
1368
1369static void
1370actionSetup( tr_upnp_action_t * action, const char * name, int prealloc )
1371{
1372    action->name = strdup( name );
1373    assert( NULL == action->args );
1374    action->args = malloc( sizeof( *action->args ) * prealloc );
1375    memset( action->args, 0, sizeof( *action->args ) * prealloc );
1376    action->len = prealloc;
1377}
1378
1379static void
1380actionFree( tr_upnp_action_t * act )
1381{
1382    free( act->name );
1383    while( 0 < act->len )
1384    {
1385        act->len--;
1386        free( act->args[act->len].name );
1387        free( act->args[act->len].var );
1388    }
1389    free( act->args );
1390}
1391
1392static int
1393actionAdd( tr_upnp_action_t * act, char * name, char * var, char dir )
1394{
1395    int    ii;
1396    void * newbuf;
1397
1398    assert( 'i' == dir || 'o' == dir );
1399
1400    ii = 0;
1401    while( ii < act->len && NULL != act->args[ii].name )
1402    {
1403        ii++;
1404    }
1405
1406    if( ii == act->len )
1407    {
1408        newbuf = realloc( act->args, sizeof( *act->args ) * ( act->len + 1 ) );
1409        if( NULL == newbuf )
1410        {
1411            return 1;
1412        }
1413        act->args = newbuf;
1414        act->len++;
1415    }
1416
1417    act->args[ii].name = name;
1418    act->args[ii].var = var;
1419    act->args[ii].dir = dir;
1420
1421    return 0;
1422}
1423
1424static const char *
1425actionLookup( tr_upnp_action_t * act, const char * key, int len,
1426              char dir, int getname )
1427{
1428    int ii;
1429
1430    assert( 'i' == dir || 'o' == dir );
1431
1432    if( NULL == key || 0 == len )
1433    {
1434        return NULL;
1435    }
1436    if( 0 > len )
1437    {
1438        len = strlen( key );
1439    }
1440
1441    for( ii = 0; ii < act->len; ii++ )
1442    {
1443        if( NULL != act->args[ii].name && dir == act->args[ii].dir )
1444        {
1445            if( !getname &&
1446                0 == tr_strncasecmp( act->args[ii].name, key, len ) )
1447            {
1448                return act->args[ii].var;
1449            }
1450            else if( getname &&
1451                     0 == tr_strncasecmp( act->args[ii].var, key, len ) )
1452            {
1453                return act->args[ii].name;
1454            }
1455        }
1456    }
1457
1458    return NULL;
1459}
1460
1461#if 0
1462/* this code is used for standalone root parsing for debugging purposes */
1463/* cc -g -Wall -D__TRANSMISSION__ -o upnp upnp.c xml.c utils.c */
1464int
1465main( int argc, char * argv[] )
1466{
1467    struct stat sb;
1468    char      * data, * soap, * scpd;
1469    int         fd, ppp;
1470    ssize_t     res;
1471
1472    if( 3 != argc )
1473    {
1474        printf( "usage: %s root-url root-file\n", argv[0] );
1475        return 0;
1476    }
1477
1478    tr_msgInit();
1479    tr_setMessageLevel( 9 );
1480
1481    if( 0 > stat( argv[2], &sb ) )
1482    {
1483        tr_err( "failed to stat file %s: %s", argv[2], strerror( errno ) );
1484        return 1;
1485    }
1486
1487    data = malloc( sb.st_size );
1488    if( NULL == data )
1489    {
1490        tr_err( "failed to malloc %zd bytes", ( size_t )sb.st_size );
1491        return 1;
1492    }
1493
1494    fd = open( argv[2], O_RDONLY );
1495    if( 0 > fd )
1496    {
1497        tr_err( "failed to open file %s: %s", argv[2], strerror( errno ) );
1498        free( data );
1499        return 1;
1500    }
1501
1502    res = read( fd, data, sb.st_size );
1503    if( sb.st_size > res )
1504    {
1505        tr_err( "failed to read file %s: %s", argv[2],
1506                ( 0 > res ? strerror( errno ) : "short read count" ) );
1507        close( fd );
1508        free( data );
1509        return 1;
1510    }
1511
1512    close( fd );
1513
1514    if( parseRoot( argv[1], data, sb.st_size, &soap, &scpd, &ppp ) )
1515    {
1516        tr_err( "root parsing failed" );
1517    }
1518    else
1519    {
1520        tr_err( "soap=%s scpd=%s ppp=%s", soap, scpd, ( ppp ? "yes" : "no" ) );
1521        free( soap );
1522        free( scpd );
1523    }
1524    free( data );
1525
1526    return 0;
1527}
1528
1529int  tr_netMcastOpen( int port, struct in_addr addr ) { assert( 0 ); }
1530int  tr_netBind    ( int port, int type ) { assert( 0 ); }
1531void tr_netClose   ( int s ) { assert( 0 ); }
1532int  tr_netRecvFrom( int s, uint8_t * buf, int size, struct sockaddr_in * sin ) { assert( 0 ); }
1533int         tr_httpRequestType( const char * data, int len,
1534                                char ** method, char ** uri ) { assert( 0 ); }
1535int         tr_httpResponseCode( const char * data, int len ) { assert( 0 ); }
1536char *      tr_httpParse( const char * data, int len, tr_http_header_t *headers ) { assert( 0 ); }
1537int         tr_httpIsUrl( const char * u, int l ) { assert( 0 ); }
1538int         tr_httpParseUrl( const char * u, int l, char ** h, int * p, char ** q ) { assert( 0 ); }
1539tr_http_t   * tr_httpClient( int t, const char * h, int p, const char * u, ... ) { assert( 0 ); }
1540tr_http_t   * tr_httpClientUrl( int t, const char * u, ... ) { assert( 0 ); }
1541void          tr_httpAddHeader( tr_http_t * h, const char * n, const char * v ) { assert( 0 ); }
1542void          tr_httpAddBody( tr_http_t * h, const char * b, ... ) { assert( 0 ); }
1543tr_tristate_t tr_httpPulse( tr_http_t * h, const char ** b, int * l ) { assert( 0 ); }
1544char        * tr_httpWhatsMyAddress( tr_http_t * h ) { assert( 0 ); }
1545void          tr_httpClose( tr_http_t * h ) { assert( 0 ); }
1546
1547void tr_lockInit     ( tr_lock_t * l ) {}
1548int  pthread_mutex_lock( pthread_mutex_t * m ) { return 0; }
1549int  pthread_mutex_unlock( pthread_mutex_t * m ) { return 0; }
1550
1551#endif
Note: See TracBrowser for help on using the repository browser.