Coverage Report

Created: 2026-11-12 06:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pupnp/upnp/src/genlib/miniserver/miniserver.c
Line
Count
Source
1
/**************************************************************************
2
 *
3
 * Copyright (c) 2000-2003 Intel Corporation
4
 * All rights reserved.
5
 * Copyright (C) 2012 France Telecom All rights reserved.
6
 *
7
 * Redistribution and use in source and binary forms, with or without
8
 * modification, are permitted provided that the following conditions are met:
9
 *
10
 * - Redistributions of source code must retain the above copyright notice,
11
 * this list of conditions and the following disclaimer.
12
 * - Redistributions in binary form must reproduce the above copyright notice,
13
 * this list of conditions and the following disclaimer in the documentation
14
 * and/or other materials provided with the distribution.
15
 * - Neither name of Intel Corporation nor the names of its contributors
16
 * may be used to endorse or promote products derived from this software
17
 * without specific prior written permission.
18
 *
19
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR
23
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
27
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
 *
31
 **************************************************************************/
32
33
#include "LinkedList.h"
34
#include "UpnpGlobal.h"
35
#include "UpnpInet.h"
36
#include "config.h"
37
#include "httpparser.h"
38
#include "membuffer.h"
39
#include "sock.h"
40
#include "upnp.h"
41
#include "upnpdebug.h"
42
#include "upnpdebug_internal.h"
43
#include <errno.h>
44
#include <stdint.h>
45
#include <time.h>
46
47
#if EXCLUDE_MINISERVER == 0
48
49
/*!
50
 * \file
51
 *
52
 * \brief Implements the functionality and utility functions
53
 * used by the Miniserver module.
54
 *
55
 * The miniserver is a central point for processing all network requests.
56
 * It is made of:
57
 *   - The SSDP sockets for discovery.
58
 *   - The HTTP listeners for description / control / eventing.
59
 *
60
 */
61
62
  #include "miniserver.h"
63
64
  #include "ThreadPool.h"
65
  #include "httpreadwrite.h"
66
  #include "ithread.h"
67
  #include "ssdplib.h"
68
  #include "statcodes.h"
69
  #include "unixutil.h" /* for socklen_t, EAFNOSUPPORT */ // IWYU pragma: keep
70
  #include "upnpapi.h"
71
  #include "upnputil.h"
72
73
  #include <assert.h>
74
  #include <errno.h>
75
  #ifndef _WIN32
76
    #include <netinet/tcp.h> /* TCP_NODELAY */
77
  #endif
78
  #include <stdio.h>
79
  #include <stdlib.h>
80
  #include <string.h>
81
  #include <sys/types.h>
82
83
  #ifndef _WIN32
84
    #include <poll.h>
85
1
    #define _poll(fds, nfds, timeout) poll(fds, nfds, timeout)
86
  #endif
87
88
  /*! . */
89
3
  #define APPLICATION_LISTENING_PORT 49152
90
91
struct mserv_request_t
92
{
93
  /*! Connection handle. */
94
  SOCKET sock;
95
  /*! . */
96
  struct sockaddr_storage foreign_sockaddr;
97
};
98
99
/*! . */
100
typedef enum
101
{
102
  /*! . */
103
  MSERV_IDLE,
104
  /*! . */
105
  MSERV_RUNNING,
106
  /*! . */
107
  MSERV_STOPPING
108
} MiniServerState;
109
110
/*! . */
111
static uint16_t miniStopSockPort;
112
113
  /*!
114
   * module vars
115
   */
116
  /* MSVC does not implement the C11 _Atomic qualifier. */
117
  #ifdef _MSC_VER
118
static MiniServerState gMServState = MSERV_IDLE;
119
  #else
120
static _Atomic MiniServerState gMServState = MSERV_IDLE;
121
  #endif
122
123
/*! Global list to track active socket connections */
124
static LinkedList gActiveConnections;
125
/*! Protects gActiveConnections and gActiveConnectionsInitialized. It is never
126
 * destroyed: workers that are still serving connections when the miniserver
127
 * stops take it to remove their connection from the list. */
128
static ithread_mutex_t gActiveConnectionsMutex = PTHREAD_MUTEX_INITIALIZER;
129
static int gActiveConnectionsInitialized = 0;
130
131
/*! Structure to hold active connection info */
132
struct active_connection_t
133
{
134
  SOCKET socket;
135
  time_t connect_time;
136
  /*! Address of the peer that opened the connection. */
137
  struct sockaddr_storage peer;
138
};
139
140
/*----------------------------------------------------------------------------*/
141
  #ifdef INTERNAL_WEB_SERVER
142
static MiniServerCallback gGetCallback = NULL;
143
static MiniServerCallback gSoapCallback = NULL;
144
static MiniServerCallback gGenaCallback = NULL;
145
146
static const int ENABLE_IPV6 =
147
    #ifdef UPNP_ENABLE_IPV6
148
  1;
149
    #else  /* UPNP_ENABLE_IPV6 */
150
  0;
151
    #endif /* UPNP_ENABLE_IPV6 */
152
153
static const int MINISERVER_REUSEADDR =
154
    #ifdef UPNP_MINISERVER_REUSEADDR
155
  1;
156
    #else  /* UPNP_MINISERVER_REUSEADDR */
157
  0;
158
    #endif /* UPNP_MINISERVER_REUSEADDR */
159
160
struct s_SocketStuff
161
{
162
  int ip_version;
163
  const char *text_addr;
164
  struct sockaddr_storage ss;
165
  union
166
  {
167
    struct sockaddr *serverAddr;
168
    struct sockaddr_in *serverAddr4;
169
    struct sockaddr_in6 *serverAddr6;
170
  };
171
  SOCKET fd;
172
  uint16_t try_port;
173
  uint16_t actual_port;
174
  socklen_t address_len;
175
};
176
177
void SetHTTPGetCallback(MiniServerCallback callback)
178
1
{
179
1
  gGetCallback = callback;
180
1
}
181
182
    #ifdef INCLUDE_DEVICE_APIS
183
1
void SetSoapCallback(MiniServerCallback callback) { gSoapCallback = callback; }
184
    #endif /* INCLUDE_DEVICE_APIS */
185
186
1
void SetGenaCallback(MiniServerCallback callback) { gGenaCallback = callback; }
187
188
static int host_header_is_numeric(char *host_port, size_t host_port_size)
189
0
{
190
0
  int rc = 0;
191
0
  struct in6_addr addr;
192
0
  char *s;
193
194
  /* Remove the port part. */
195
0
  s = host_port + host_port_size - 1;
196
0
  while (s != host_port && *s != ']' && *s != ':') {
197
0
    --s;
198
0
  }
199
0
  if (*s == ':') {
200
0
    *s = 0;
201
0
  } else {
202
0
    s = host_port + host_port_size;
203
0
  }
204
205
  /* Try IPV4 */
206
0
  rc = inet_pton(AF_INET, host_port, &addr);
207
0
  if (rc == 1) {
208
0
    goto ExitFunction;
209
0
  }
210
  /* Try IPV6 */
211
  /* Check for and remove the square brackets. */
212
0
  if (strlen(host_port) < 3 || host_port[0] != '[' || *(s - 1) != ']') {
213
0
    rc = 0;
214
0
    goto ExitFunction;
215
0
  }
216
0
  *(s - 1) = '\0';
217
0
  rc = inet_pton(AF_INET6, host_port + 1, &addr) == 1;
218
219
0
ExitFunction:
220
0
  return rc;
221
0
}
222
223
static int getNumericHostRedirection(
224
  int socket, char *host_port, size_t hp_size)
225
0
{
226
0
  int rc;
227
0
  const char *rcp;
228
0
  struct sockaddr_storage addr;
229
0
  struct sockaddr_in *addr4 = (struct sockaddr_in *)&addr;
230
0
  struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&addr;
231
0
  socklen_t addr_len = sizeof addr;
232
0
  in_port_t port;
233
0
  char host[INET6_ADDRSTRLEN];
234
0
  int n;
235
236
0
  rc = getsockname(socket, (struct sockaddr *)&addr, &addr_len);
237
0
  if (rc) {
238
0
    goto ExitFunction;
239
0
  }
240
0
  switch (addr.ss_family) {
241
0
  case AF_INET6:
242
0
    rcp = inet_ntop(AF_INET6, &addr6->sin6_addr, host, sizeof host);
243
0
    if (!rcp) {
244
0
      rc = -1;
245
0
      goto ExitFunction;
246
0
    }
247
0
    port = ntohs(addr6->sin6_port);
248
0
    n = snprintf(host_port, hp_size, "[%s]:%d", host, port);
249
0
    break;
250
0
  case AF_INET:
251
0
  default:
252
0
    rcp = inet_ntop(AF_INET, &addr4->sin_addr, host, sizeof host);
253
0
    if (!rcp) {
254
0
      rc = -1;
255
0
      goto ExitFunction;
256
0
    }
257
0
    port = ntohs(addr4->sin_port);
258
0
    n = snprintf(host_port, hp_size, "%s:%d", host, port);
259
0
    break;
260
0
  }
261
0
  if (n < 0) {
262
0
    rc = -1;
263
0
  }
264
265
0
ExitFunction:
266
0
  return rc == 0;
267
0
}
268
269
/*!
270
 * \brief Based on the type pf message, appropriate callback is issued.
271
 *
272
 * \return 0 on Success or HTTP_INTERNAL_SERVER_ERROR if Callback is NULL.
273
 */
274
static int dispatch_request(
275
  /*! [in] Socket Information object. */
276
  SOCKINFO *info,
277
  /*! [in] HTTP parser object. */
278
  http_parser_t *parser)
279
0
{
280
0
  memptr header;
281
0
  size_t min_size;
282
0
  http_message_t *request;
283
0
  MiniServerCallback callback;
284
0
  WebCallback_HostValidate host_validate_callback = 0;
285
0
  void *cookie = 0;
286
0
  int rc = UPNP_E_SUCCESS;
287
  /* If it does not fit in here, it is likely invalid anyway. */
288
0
  char host_port[NAME_SIZE];
289
290
0
  switch (parser->msg.method) {
291
  /* Soap Call */
292
0
  case SOAPMETHOD_POST:
293
0
  case HTTPMETHOD_MPOST:
294
0
    callback = gSoapCallback;
295
0
    UpnpPrintf(UPNP_INFO,
296
0
      MSERV,
297
0
      __FILE__,
298
0
      __LINE__,
299
0
      "miniserver %d: got SOAP msg\n",
300
0
      info->socket);
301
0
    break;
302
  /* Gena Call */
303
0
  case HTTPMETHOD_NOTIFY:
304
0
  case HTTPMETHOD_SUBSCRIBE:
305
0
  case HTTPMETHOD_UNSUBSCRIBE:
306
0
    callback = gGenaCallback;
307
0
    UpnpPrintf(UPNP_INFO,
308
0
      MSERV,
309
0
      __FILE__,
310
0
      __LINE__,
311
0
      "miniserver %d: got GENA msg\n",
312
0
      info->socket);
313
0
    break;
314
  /* HTTP server call */
315
0
  case HTTPMETHOD_GET:
316
0
  case HTTPMETHOD_POST:
317
0
  case HTTPMETHOD_HEAD:
318
0
  case HTTPMETHOD_SIMPLEGET:
319
0
    callback = gGetCallback;
320
0
    host_validate_callback = gWebCallback_HostValidate;
321
0
    cookie = gWebCallback_HostValidateCookie;
322
0
    UpnpPrintf(UPNP_INFO,
323
0
      MSERV,
324
0
      __FILE__,
325
0
      __LINE__,
326
0
      "miniserver %d: got WEB server msg\n",
327
0
      info->socket);
328
0
    break;
329
0
  default:
330
0
    callback = 0;
331
0
  }
332
0
  if (!callback) {
333
0
    rc = HTTP_INTERNAL_SERVER_ERROR;
334
0
    goto ExitFunction;
335
0
  }
336
0
  request = &parser->msg;
337
    #ifdef DEBUG_REDIRECT
338
  /*--------------------------------------------------------------------*/
339
  getNumericHostRedirection(info->socket, host_port, sizeof host_port);
340
  UpnpPrintf(UPNP_INFO,
341
    MSERV,
342
    __FILE__,
343
    __LINE__,
344
    "DEBUG TEST: Redirect host_port = %s.\n",
345
    host_port);
346
    #endif /* DEBUG_REDIRECT */
347
  /*--------------------------------------------------------------------*/
348
  /* check HOST header for an IP number -- prevents DNS rebinding. */
349
0
  if (!httpmsg_find_hdr(request, HDR_HOST, &header)) {
350
0
    rc = UPNP_E_BAD_HTTPMSG;
351
0
    goto ExitFunction;
352
0
  }
353
0
  min_size = header.length < ((sizeof host_port) - 1)
354
0
         ? header.length
355
0
         : (sizeof host_port) - 1;
356
0
  memcpy(host_port, header.buf, min_size);
357
0
  host_port[min_size] = 0;
358
0
  if (host_validate_callback) {
359
0
    rc = host_validate_callback(host_port, cookie);
360
0
    if (rc == UPNP_E_BAD_HTTPMSG) {
361
0
      goto ExitFunction;
362
0
    }
363
0
  } else if (!host_header_is_numeric(host_port, min_size)) {
364
0
    if (!gAllowLiteralHostRedirection) {
365
0
      rc = UPNP_E_BAD_HTTPMSG;
366
0
      UpnpPrintf(UPNP_INFO,
367
0
        MSERV,
368
0
        __FILE__,
369
0
        __LINE__,
370
0
        "Possible DNS Rebind attack prevented.\n");
371
0
      goto ExitFunction;
372
0
    } else {
373
0
      membuffer redir_buf;
374
0
      char redir_str[2 * NAME_SIZE];
375
0
      int timeout = HTTP_DEFAULT_TIMEOUT;
376
377
0
      getNumericHostRedirection(
378
0
        (int)info->socket, host_port, sizeof host_port);
379
0
      membuffer_init(&redir_buf);
380
0
      snprintf(redir_str,
381
0
        sizeof redir_str,
382
0
        "HTTP/1.1 307 Temporary Redirect\r\n"
383
0
        "Location: http://%s\r\n\r\n",
384
0
        host_port);
385
0
      membuffer_append_str(&redir_buf, redir_str);
386
0
      rc = http_SendMessage(info,
387
0
        &timeout,
388
0
        "b",
389
0
        redir_buf.buf,
390
0
        redir_buf.length);
391
0
      membuffer_destroy(&redir_buf);
392
0
      goto ExitFunction;
393
0
    }
394
0
  }
395
0
  callback(parser, request, info);
396
397
0
ExitFunction:
398
0
  return rc;
399
0
}
400
401
/*!
402
 * \brief Send Error Message.
403
 */
404
static UPNP_INLINE void handle_error(
405
  /*! [in] Socket Information object. */
406
  SOCKINFO *info,
407
  /*! [in] HTTP Error Code. */
408
  int http_error_code,
409
  /*! [in] Major Version Number. */
410
  int major,
411
  /*! [in] Minor Version Number. */
412
  int minor)
413
0
{
414
0
  http_SendStatusResponse(info, http_error_code, major, minor);
415
0
}
416
417
/*!
418
 * \brief Compare function for active connections, return true if equal (found)
419
 */
420
static int active_connection_cmp(void *first, void *second)
421
0
{
422
0
  struct active_connection_t *conn1 = (struct active_connection_t *)first;
423
0
  struct active_connection_t *conn2 =
424
0
    (struct active_connection_t *)second;
425
426
  /* Return non-zero (true) if sockets match */
427
0
  return (conn1->socket == conn2->socket);
428
0
}
429
430
/*!
431
 * \brief Tells whether two socket addresses are the same host, ignoring the
432
 * port. The IPv6 socket of the miniserver is IPv6 only, so there are no
433
 * IPv4-mapped addresses to take into account.
434
 */
435
static int same_peer_address(const struct sockaddr *a, const struct sockaddr *b)
436
0
{
437
0
  if (a->sa_family != b->sa_family) {
438
0
    return 0;
439
0
  }
440
0
  if (a->sa_family == AF_INET) {
441
0
    return ((const struct sockaddr_in *)a)->sin_addr.s_addr ==
442
0
           ((const struct sockaddr_in *)b)->sin_addr.s_addr;
443
0
  }
444
0
  if (a->sa_family == AF_INET6) {
445
0
    return memcmp(&((const struct sockaddr_in6 *)a)->sin6_addr,
446
0
             &((const struct sockaddr_in6 *)b)->sin6_addr,
447
0
             sizeof(struct in6_addr)) == 0;
448
0
  }
449
450
0
  return 0;
451
0
}
452
453
/*!
454
 * \brief Add a socket to the active connections list, unless its peer
455
 * already holds MINISERVER_MAX_CONNECTIONS_PER_PEER of them.
456
 *
457
 * \return 1 if the connection is refused, 0 otherwise.
458
 */
459
static int add_active_connection(SOCKET sock, const struct sockaddr *peer)
460
0
{
461
0
  struct active_connection_t *conn;
462
0
  ListNode *node;
463
0
  int from_peer = 0;
464
465
0
  conn = (struct active_connection_t *)malloc(
466
0
    sizeof(struct active_connection_t));
467
0
  if (conn) {
468
0
    conn->socket = sock;
469
0
    conn->connect_time = time(NULL);
470
0
    memcpy(&conn->peer, peer, sizeof(conn->peer));
471
472
0
    ithread_mutex_lock(&gActiveConnectionsMutex);
473
0
    if (!gActiveConnectionsInitialized) {
474
0
      ListInit(&gActiveConnections,
475
0
        active_connection_cmp,
476
0
        free);
477
0
      gActiveConnectionsInitialized = 1;
478
0
    }
479
    /* Count the connections the peer already holds. */
480
0
    for (node = ListHead(&gActiveConnections); node;
481
0
      node = ListNext(&gActiveConnections, node)) {
482
0
      const struct active_connection_t *other =
483
0
        (const struct active_connection_t *)node->item;
484
485
0
      if (same_peer_address(
486
0
            (const struct sockaddr *)&other->peer,
487
0
            peer)) {
488
0
        from_peer++;
489
0
      }
490
0
    }
491
0
    if (from_peer >= MINISERVER_MAX_CONNECTIONS_PER_PEER) {
492
0
      ithread_mutex_unlock(&gActiveConnectionsMutex);
493
0
      free(conn);
494
495
0
      return 1;
496
0
    }
497
0
    ListAddTail(&gActiveConnections, conn);
498
0
    ithread_mutex_unlock(&gActiveConnectionsMutex);
499
500
0
    UpnpPrintf(UPNP_INFO,
501
0
      MSERV,
502
0
      __FILE__,
503
0
      __LINE__,
504
0
      "Added active connection: socket %d\n",
505
0
      sock);
506
0
  }
507
508
0
  return 0;
509
0
}
510
511
/*!
512
 * \brief Remove a socket from the active connections list
513
 */
514
static void remove_active_connection(SOCKET sock)
515
0
{
516
0
  struct active_connection_t search_conn;
517
0
  ListNode *node;
518
519
  /* Create a temporary connection structure for searching */
520
0
  search_conn.socket = sock;
521
0
  search_conn.connect_time = 0; /* Not used for comparison */
522
523
0
  ithread_mutex_lock(&gActiveConnectionsMutex);
524
  /* The list is gone once the miniserver has stopped. */
525
0
  node = gActiveConnectionsInitialized
526
0
           ? ListFind(&gActiveConnections, NULL, &search_conn)
527
0
           : NULL;
528
0
  if (node) {
529
0
    ListDelNode(&gActiveConnections, node, 1);
530
0
    UpnpPrintf(UPNP_INFO,
531
0
      MSERV,
532
0
      __FILE__,
533
0
      __LINE__,
534
0
      "Removed active connection: socket %d\n",
535
0
      sock);
536
0
  }
537
0
  ithread_mutex_unlock(&gActiveConnectionsMutex);
538
0
}
539
540
/*!
541
 * \brief Free memory assigned for handling request and uninitialize socket
542
 * functionality.
543
 */
544
static void free_handle_request_arg(
545
  /*! [in] Request Message to be freed. */
546
  void *args)
547
0
{
548
0
  struct mserv_request_t *request = (struct mserv_request_t *)args;
549
550
0
  remove_active_connection(request->sock);
551
0
  sock_close(request->sock);
552
0
  free(request);
553
0
}
554
555
/*!
556
 * \brief Waits for the first byte (or the end of file) of a connection.
557
 *
558
 * \return 1 when the socket can be read, 0 on timeout or error.
559
 */
560
static int wait_for_first_byte(
561
  /*! [in] Connection to wait on. */
562
  SOCKET sock,
563
  /*! [in] Seconds to wait. */
564
  int timeout_secs)
565
0
{
566
0
  int ret;
567
    #ifdef _WIN32
568
  fd_set readfds;
569
  struct timeval tv;
570
571
  FD_ZERO(&readfds);
572
  FD_SET(sock, &readfds);
573
  tv.tv_sec = timeout_secs;
574
  tv.tv_usec = 0;
575
  ret = select((int)sock + 1, &readfds, NULL, NULL, &tv);
576
    #else
577
0
  struct pollfd pfd;
578
579
0
  pfd.fd = sock;
580
0
  pfd.events = POLLIN;
581
0
  do {
582
0
    pfd.revents = 0;
583
0
    ret = _poll(&pfd, 1, timeout_secs * 1000);
584
0
  } while (ret < 0 && errno == EINTR);
585
0
    #endif
586
587
0
  return ret > 0;
588
0
}
589
590
/*!
591
 * \brief Receive the request and dispatch it for handling.
592
 */
593
static void handle_request(
594
  /*! [in] Request Message to be handled. */
595
  void *args)
596
0
{
597
0
  SOCKINFO info;
598
0
  int http_error_code;
599
0
  int ret_code;
600
0
  int major = 1;
601
0
  int minor = 1;
602
0
  http_parser_t parser;
603
0
  http_message_t *h_msg = NULL;
604
0
  int timeout = HTTP_DEFAULT_TIMEOUT;
605
0
  struct mserv_request_t *request = (struct mserv_request_t *)args;
606
0
  SOCKET sock = request->sock;
607
608
0
  UpnpPrintf(UPNP_INFO,
609
0
    MSERV,
610
0
    __FILE__,
611
0
    __LINE__,
612
0
    "miniserver %d: READING\n",
613
0
    sock);
614
  /* parser_request_init( &parser ); */ /* LEAK_FIX_MK */
615
0
  h_msg = &parser.msg;
616
0
  ret_code = sock_init_with_ip(
617
0
    &info, sock, (struct sockaddr *)&request->foreign_sockaddr);
618
0
  if (ret_code != UPNP_E_SUCCESS) {
619
0
    remove_active_connection(sock);
620
0
    sock_close(sock);
621
0
    free(request);
622
0
    httpmsg_destroy(h_msg);
623
0
    return;
624
0
  }
625
  /* A client that connects and stays silent must not keep this worker
626
   * busy for the whole receive timeout. */
627
0
  if (!wait_for_first_byte(sock, MINISERVER_FIRST_BYTE_TIMEOUT)) {
628
0
    UpnpPrintf(UPNP_INFO,
629
0
      MSERV,
630
0
      __FILE__,
631
0
      __LINE__,
632
0
      "miniserver %d: no request within %d s, closing\n",
633
0
      sock,
634
0
      MINISERVER_FIRST_BYTE_TIMEOUT);
635
0
    remove_active_connection(sock);
636
0
    sock_destroy(&info, SD_BOTH);
637
0
    free(request);
638
0
    return;
639
0
  }
640
  /* read */
641
0
  ret_code = http_RecvMessage(
642
0
    &info, &parser, HTTPMETHOD_UNKNOWN, &timeout, &http_error_code);
643
0
  if (ret_code != 0) {
644
0
    goto error_handler;
645
0
  }
646
0
  UpnpPrintf(UPNP_INFO,
647
0
    MSERV,
648
0
    __FILE__,
649
0
    __LINE__,
650
0
    "miniserver %d: PROCESSING...\n",
651
0
    sock);
652
  /* dispatch */
653
0
  http_error_code = dispatch_request(&info, &parser);
654
0
  if (http_error_code != 0) {
655
0
    goto error_handler;
656
0
  }
657
0
  http_error_code = 0;
658
659
0
error_handler:
660
0
  if (http_error_code > 0) {
661
0
    if (h_msg) {
662
0
      major = h_msg->major_version;
663
0
      minor = h_msg->minor_version;
664
0
    }
665
0
    handle_error(&info, http_error_code, major, minor);
666
0
  }
667
0
  remove_active_connection(info.socket);
668
0
  sock_destroy(&info, SD_BOTH);
669
0
  httpmsg_destroy(h_msg);
670
0
  free(request);
671
672
0
  UpnpPrintf(UPNP_INFO,
673
0
    MSERV,
674
0
    __FILE__,
675
0
    __LINE__,
676
0
    "miniserver %d: COMPLETE\n",
677
0
    sock);
678
0
}
679
680
/*!
681
 * \brief Initialize the thread pool to handle a request, sets priority for the
682
 * job and adds the job to the thread pool.
683
 */
684
static UPNP_INLINE void schedule_request_job(
685
  /*! [in] Socket Descriptor on which connection is accepted. */
686
  SOCKET sock,
687
  /*! [in] Clients Address information. */
688
  struct sockaddr *clientAddr)
689
0
{
690
0
  struct mserv_request_t *request;
691
0
  ThreadPoolJob job;
692
693
0
  memset(&job, 0, sizeof(job));
694
695
0
  request = (struct mserv_request_t *)malloc(
696
0
    sizeof(struct mserv_request_t));
697
0
  if (!request) {
698
0
    UpnpPrintf(UPNP_INFO,
699
0
      MSERV,
700
0
      __FILE__,
701
0
      __LINE__,
702
0
      "mserv %d: out of memory\n",
703
0
      sock);
704
0
    sock_close(sock);
705
0
    return;
706
0
  }
707
708
0
  request->sock = sock;
709
0
  memcpy(&request->foreign_sockaddr,
710
0
    clientAddr,
711
0
    sizeof(request->foreign_sockaddr));
712
0
  TPJobInit(&job, handle_request, (void *)request);
713
0
  TPJobSetFreeFunction(&job, free_handle_request_arg);
714
0
  TPJobSetPriority(&job, MED_PRIORITY);
715
716
  /* Add the connection to active connections list, unless its peer
717
   * already holds its share of them. */
718
0
  if (add_active_connection(sock, clientAddr)) {
719
0
    UpnpPrintf(UPNP_INFO,
720
0
      MSERV,
721
0
      __FILE__,
722
0
      __LINE__,
723
0
      "mserv %d: too many connections from this peer, "
724
0
      "closing\n",
725
0
      sock);
726
0
    free(request);
727
0
    sock_close(sock);
728
0
    return;
729
0
  }
730
731
0
  if (ThreadPoolAdd(&gMiniServerThreadPool, &job, NULL) != 0) {
732
0
    UpnpPrintf(UPNP_INFO,
733
0
      MSERV,
734
0
      __FILE__,
735
0
      __LINE__,
736
0
      "mserv %d: cannot schedule request\n",
737
0
      sock);
738
0
    remove_active_connection(sock);
739
0
    free(request);
740
0
    sock_close(sock);
741
0
    return;
742
0
  }
743
0
}
744
  #endif /* INTERNAL_WEB_SERVER */
745
/*--------------------------------------------------------------------*/
746
747
/*!
748
 * \brief Shutdown all active socket connections
749
 */
750
void shutdown_all_active_connections(void)
751
0
{
752
  /*--------------------------------------------------------------------*/
753
0
  #ifdef INTERNAL_WEB_SERVER
754
0
  ListNode *node;
755
0
  int count;
756
757
0
  ithread_mutex_lock(&gActiveConnectionsMutex);
758
0
  if (!gActiveConnectionsInitialized) {
759
0
    ithread_mutex_unlock(&gActiveConnectionsMutex);
760
0
    return;
761
0
  }
762
0
  UpnpPrintf(UPNP_INFO,
763
0
    MSERV,
764
0
    __FILE__,
765
0
    __LINE__,
766
0
    "Shutting down all active socket connections\n");
767
768
0
  node = ListHead(&gActiveConnections);
769
0
  count = 0;
770
0
  while (node) {
771
0
    struct active_connection_t *conn =
772
0
      (struct active_connection_t *)node->item;
773
0
    if (conn && conn->socket != INVALID_SOCKET) {
774
0
      UpnpPrintf(UPNP_INFO,
775
0
        MSERV,
776
0
        __FILE__,
777
0
        __LINE__,
778
0
        "Force shutting down socket %d\n",
779
0
        conn->socket);
780
0
      shutdown(conn->socket, SD_BOTH);
781
0
      count++;
782
0
    }
783
0
    node = ListNext(&gActiveConnections, node);
784
0
  }
785
786
  /* Destroy the list and reset state. The mutex stays, workers still use
787
   * it. */
788
0
  ListDestroy(&gActiveConnections, 1);
789
0
  gActiveConnectionsInitialized = 0;
790
0
  ithread_mutex_unlock(&gActiveConnectionsMutex);
791
792
0
  UpnpPrintf(UPNP_INFO,
793
0
    MSERV,
794
0
    __FILE__,
795
0
    __LINE__,
796
0
    "Shutdown %d active socket connections and clean\n",
797
0
    count);
798
0
  #endif /* INTERNAL_WEB_SERVER */
799
  /*--------------------------------------------------------------------*/
800
0
}
801
802
  #ifdef _WIN32
803
static UPNP_INLINE void fdset_if_valid(SOCKET sock, fd_set *set)
804
{
805
  if (sock != INVALID_SOCKET) {
806
    FD_SET(sock, set);
807
  }
808
}
809
  #else
810
static UPNP_INLINE void pollfd_if_valid(
811
  SOCKET sock, struct pollfd *pfd, short events)
812
9
{
813
9
  if (sock != INVALID_SOCKET) {
814
4
    pfd->fd = sock;
815
4
    pfd->events = events;
816
4
    pfd->revents = 0;
817
5
  } else {
818
5
    pfd->fd = -1;
819
5
    pfd->events = 0;
820
5
    pfd->revents = 0;
821
5
  }
822
9
}
823
  #endif /* _WIN32 */
824
825
  #ifdef _WIN32
826
static void ssdp_read(SOCKET *read_sock, fd_set *set)
827
{
828
  if (*read_sock != INVALID_SOCKET && FD_ISSET(*read_sock, set)) {
829
    int ret = readFromSSDPSocket(*read_sock);
830
    if (ret != 0) {
831
      UpnpPrintf(UPNP_INFO,
832
        MSERV,
833
        __FILE__,
834
        __LINE__,
835
        "miniserver: Error in readFromSSDPSocket(%d): "
836
        "closing socket\n",
837
        *read_sock);
838
      sock_close(*read_sock);
839
      *read_sock = INVALID_SOCKET;
840
    }
841
  }
842
}
843
static void web_server_accept(SOCKET listen_sock, fd_set *set)
844
{
845
    /*--------------------------------------------------------------------*/
846
    #ifdef INTERNAL_WEB_SERVER
847
  SOCKET asock;
848
  socklen_t clientLen;
849
  struct sockaddr_storage clientAddr;
850
  char errorBuffer[ERROR_BUFFER_LEN];
851
852
  if (listen_sock != INVALID_SOCKET && FD_ISSET(listen_sock, set)) {
853
    clientLen = sizeof(clientAddr);
854
    asock = accept(listen_sock,
855
      (struct sockaddr *)&clientAddr,
856
      &clientLen);
857
858
    if (asock == INVALID_SOCKET) {
859
      strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
860
      UpnpPrintf(UPNP_INFO,
861
        MSERV,
862
        __FILE__,
863
        __LINE__,
864
        "miniserver: Error in accept(): %s\n",
865
        errorBuffer);
866
    } else {
867
      const int on = 1;
868
      setsockopt(asock,
869
        IPPROTO_TCP,
870
        TCP_NODELAY,
871
        (const char *)&on,
872
        sizeof(on));
873
      schedule_request_job(
874
        asock, (struct sockaddr *)&clientAddr);
875
    }
876
  }
877
    #endif /* INTERNAL_WEB_SERVER */
878
  /*--------------------------------------------------------------------*/
879
}
880
  #else /* _WIN32 */
881
static void web_server_accept(SOCKET listen_sock, struct pollfd *pfd)
882
0
{
883
    /*--------------------------------------------------------------------*/
884
0
    #ifdef INTERNAL_WEB_SERVER
885
0
  SOCKET asock;
886
0
  socklen_t clientLen;
887
0
  struct sockaddr_storage clientAddr;
888
0
  char errorBuffer[ERROR_BUFFER_LEN];
889
890
0
  if (listen_sock != INVALID_SOCKET && (pfd->revents & POLLIN)) {
891
892
0
    clientLen = sizeof(clientAddr);
893
0
    asock = accept(listen_sock,
894
0
      (struct sockaddr *)&clientAddr,
895
0
      &clientLen);
896
897
0
    if (asock == INVALID_SOCKET) {
898
0
      strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
899
0
      UpnpPrintf(UPNP_INFO,
900
0
        MSERV,
901
0
        __FILE__,
902
0
        __LINE__,
903
0
        "miniserver: Error in accept(): %s\n",
904
0
        errorBuffer);
905
0
    } else {
906
0
      const int on = 1;
907
0
      setsockopt(asock,
908
0
        IPPROTO_TCP,
909
0
        TCP_NODELAY,
910
0
        (const char *)&on,
911
0
        sizeof(on));
912
0
      schedule_request_job(
913
0
        asock, (struct sockaddr *)&clientAddr);
914
0
    }
915
0
  }
916
0
    #endif /* INTERNAL_WEB_SERVER */
917
  /*--------------------------------------------------------------------*/
918
0
}
919
  #endif         /* _WIN32 */
920
921
  #ifndef _WIN32
922
static void ssdp_read(SOCKET *read_sock, struct pollfd *pfd)
923
0
{
924
0
  if (*read_sock != INVALID_SOCKET && (pfd->revents & POLLIN)) {
925
926
0
    int ret = readFromSSDPSocket(*read_sock);
927
0
    if (ret != 0) {
928
0
      UpnpPrintf(UPNP_INFO,
929
0
        MSERV,
930
0
        __FILE__,
931
0
        __LINE__,
932
0
        "miniserver: Error in readFromSSDPSocket(%d): "
933
0
        "closing socket\n",
934
0
        *read_sock);
935
0
      sock_close(*read_sock);
936
0
      *read_sock = INVALID_SOCKET;
937
0
    }
938
0
  }
939
0
}
940
  #endif /* _WIN32 */
941
942
  #ifdef _WIN32
943
static int receive_from_stopSock(SOCKET ssock, fd_set *set)
944
{
945
  ssize_t byteReceived;
946
  socklen_t clientLen;
947
  struct sockaddr_storage clientAddr;
948
  char requestBuf[256];
949
  char buf_ntop[INET6_ADDRSTRLEN];
950
951
  if (ssock != INVALID_SOCKET && FD_ISSET(ssock, set)) {
952
    clientLen = sizeof(clientAddr);
953
    memset(&clientAddr, 0, sizeof(clientAddr));
954
955
    byteReceived = recvfrom(ssock,
956
      requestBuf,
957
      (size_t)25,
958
      0,
959
      (struct sockaddr *)&clientAddr,
960
      &clientLen);
961
962
    if (byteReceived > 0) {
963
      requestBuf[byteReceived] = '\0';
964
965
      /* Convert IPv4 address to string */
966
      inet_ntop(AF_INET,
967
        &((struct sockaddr_in *)&clientAddr)->sin_addr,
968
        buf_ntop,
969
        sizeof(buf_ntop));
970
971
      UpnpPrintf(UPNP_INFO,
972
        MSERV,
973
        __FILE__,
974
        __LINE__,
975
        "Received response: %s From host %s \n",
976
        requestBuf,
977
        buf_ntop);
978
979
      UpnpPrintf(UPNP_PACKET,
980
        MSERV,
981
        __FILE__,
982
        __LINE__,
983
        "Received multicast packet: \n %s\n",
984
        requestBuf);
985
986
      if (strstr(requestBuf, "ShutDown") != NULL) {
987
        return 1;
988
      }
989
    } else {
990
      UpnpPrintf(UPNP_INFO,
991
        MSERV,
992
        __FILE__,
993
        __LINE__,
994
        "miniserver: stopSock Error, aborting...\n");
995
      return 1;
996
    }
997
  }
998
999
  return 0;
1000
}
1001
  #else  /* _WIN32 */
1002
static int receive_from_stopSock(SOCKET ssock, struct pollfd *pfd)
1003
0
{
1004
0
  ssize_t byteReceived;
1005
0
  socklen_t clientLen;
1006
0
  struct sockaddr_storage clientAddr;
1007
0
  char requestBuf[256];
1008
0
  char buf_ntop[INET6_ADDRSTRLEN];
1009
1010
0
  if (ssock != INVALID_SOCKET && (pfd->revents & POLLIN)) {
1011
1012
0
    clientLen = sizeof(clientAddr);
1013
0
    memset(&clientAddr, 0, sizeof(clientAddr));
1014
1015
0
    byteReceived = recvfrom(ssock,
1016
0
      requestBuf,
1017
0
      (size_t)25,
1018
0
      0,
1019
0
      (struct sockaddr *)&clientAddr,
1020
0
      &clientLen);
1021
1022
0
    if (byteReceived > 0) {
1023
0
      requestBuf[byteReceived] = '\0';
1024
1025
      /* Convert IPv4 address to string */
1026
0
      inet_ntop(AF_INET,
1027
0
        &((struct sockaddr_in *)&clientAddr)->sin_addr,
1028
0
        buf_ntop,
1029
0
        sizeof(buf_ntop));
1030
1031
0
      UpnpPrintf(UPNP_INFO,
1032
0
        MSERV,
1033
0
        __FILE__,
1034
0
        __LINE__,
1035
0
        "Received response: %s From host %s \n",
1036
0
        requestBuf,
1037
0
        buf_ntop);
1038
1039
0
      UpnpPrintf(UPNP_PACKET,
1040
0
        MSERV,
1041
0
        __FILE__,
1042
0
        __LINE__,
1043
0
        "Received multicast packet: \n %s\n",
1044
0
        requestBuf);
1045
1046
0
      if (strstr(requestBuf, "ShutDown") != NULL) {
1047
0
        return 1;
1048
0
      }
1049
1050
0
    } else {
1051
0
      UpnpPrintf(UPNP_INFO,
1052
0
        MSERV,
1053
0
        __FILE__,
1054
0
        __LINE__,
1055
0
        "miniserver: stopSock Error, aborting...\n");
1056
0
      return 1;
1057
0
    }
1058
0
  }
1059
1060
0
  return 0;
1061
0
}
1062
  #endif /* _WIN32 */
1063
1064
  #define MINI_SERVER_MAX_PFDS 9
1065
/*!
1066
 * \brief Run the miniserver.
1067
 *
1068
 * The MiniServer accepts a new request and schedules a thread to handle the
1069
 * new request. Checks for socket state and invokes appropriate read and
1070
 * shutdown actions for the Miniserver and SSDP sockets.
1071
 */
1072
static void RunMiniServer(
1073
  /*! [in] Socket Array (MiniServerSockArray *). */
1074
  void *input)
1075
1
{
1076
1
  MiniServerSockArray *mini_sock = (MiniServerSockArray *)input;
1077
1
  char errorBuffer[ERROR_BUFFER_LEN];
1078
1
  int ret = 0;
1079
1
  int stopSock = 0;
1080
  #ifdef _WIN32
1081
  fd_set expSet;
1082
  fd_set rdSet;
1083
  SOCKET maxMiniSock;
1084
1085
  maxMiniSock = 0;
1086
  maxMiniSock = max(maxMiniSock, mini_sock->miniServerSock4);
1087
  maxMiniSock = max(maxMiniSock, mini_sock->miniServerSock6);
1088
  maxMiniSock = max(maxMiniSock, mini_sock->miniServerSock6UlaGua);
1089
  maxMiniSock = max(maxMiniSock, mini_sock->miniServerStopSock);
1090
  maxMiniSock = max(maxMiniSock, mini_sock->ssdpSock4);
1091
  maxMiniSock = max(maxMiniSock, mini_sock->ssdpSock6);
1092
  maxMiniSock = max(maxMiniSock, mini_sock->ssdpSock6UlaGua);
1093
    #ifdef INCLUDE_CLIENT_APIS
1094
  maxMiniSock = max(maxMiniSock, mini_sock->ssdpReqSock4);
1095
  maxMiniSock = max(maxMiniSock, mini_sock->ssdpReqSock6);
1096
    #endif /* INCLUDE_CLIENT_APIS */
1097
  ++maxMiniSock;
1098
  #else  /* _WIN32 */
1099
1
  struct pollfd fds[MINI_SERVER_MAX_PFDS];
1100
1
  nfds_t nfds = 0;
1101
1
  #endif /* _WIN32 */
1102
1103
1
  gMServState = MSERV_RUNNING;
1104
1105
2
  while (!stopSock) {
1106
  #ifdef _WIN32
1107
    FD_ZERO(&rdSet);
1108
    FD_ZERO(&expSet);
1109
    FD_SET(mini_sock->miniServerStopSock, &expSet);
1110
    FD_SET(mini_sock->miniServerStopSock, &rdSet);
1111
    fdset_if_valid(mini_sock->miniServerSock4, &rdSet);
1112
    fdset_if_valid(mini_sock->miniServerSock6, &rdSet);
1113
    fdset_if_valid(mini_sock->miniServerSock6UlaGua, &rdSet);
1114
    fdset_if_valid(mini_sock->ssdpSock4, &rdSet);
1115
    fdset_if_valid(mini_sock->ssdpSock6, &rdSet);
1116
    fdset_if_valid(mini_sock->ssdpSock6UlaGua, &rdSet);
1117
    #ifdef INCLUDE_CLIENT_APIS
1118
    fdset_if_valid(mini_sock->ssdpReqSock4, &rdSet);
1119
    fdset_if_valid(mini_sock->ssdpReqSock6, &rdSet);
1120
    #endif /* INCLUDE_CLIENT_APIS */
1121
    /* select() */
1122
    ret = select((int)maxMiniSock, &rdSet, NULL, &expSet, NULL);
1123
    if (ret == SOCKET_ERROR && errno == EINTR) {
1124
      continue;
1125
    }
1126
    if (ret == SOCKET_ERROR) {
1127
      strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
1128
      UpnpPrintf(UPNP_CRITICAL,
1129
        SSDP,
1130
        __FILE__,
1131
        __LINE__,
1132
        "Error in select(): %s\n",
1133
        errorBuffer);
1134
      continue;
1135
    }
1136
    web_server_accept(mini_sock->miniServerSock4, &rdSet);
1137
    web_server_accept(mini_sock->miniServerSock6, &rdSet);
1138
    web_server_accept(mini_sock->miniServerSock6UlaGua, &rdSet);
1139
    #ifdef INCLUDE_CLIENT_APIS
1140
    ssdp_read(&mini_sock->ssdpReqSock4, &rdSet);
1141
    ssdp_read(&mini_sock->ssdpReqSock6, &rdSet);
1142
    #endif /* INCLUDE_CLIENT_APIS */
1143
    ssdp_read(&mini_sock->ssdpSock4, &rdSet);
1144
    ssdp_read(&mini_sock->ssdpSock6, &rdSet);
1145
    ssdp_read(&mini_sock->ssdpSock6UlaGua, &rdSet);
1146
    stopSock = receive_from_stopSock(
1147
      mini_sock->miniServerStopSock, &rdSet);
1148
  #else /* _WIN32 */
1149
1
    nfds = 0;
1150
1151
    /* Stop socket: watch for read + error */
1152
1
    pollfd_if_valid(mini_sock->miniServerStopSock,
1153
1
      &fds[nfds++],
1154
1
      POLLIN | POLLERR | POLLHUP);
1155
1156
1
    pollfd_if_valid(
1157
1
      mini_sock->miniServerSock4, &fds[nfds++], POLLIN);
1158
1159
1
    pollfd_if_valid(
1160
1
      mini_sock->miniServerSock6, &fds[nfds++], POLLIN);
1161
1162
1
    pollfd_if_valid(
1163
1
      mini_sock->miniServerSock6UlaGua, &fds[nfds++], POLLIN);
1164
1165
1
    pollfd_if_valid(mini_sock->ssdpSock4, &fds[nfds++], POLLIN);
1166
1167
1
    pollfd_if_valid(mini_sock->ssdpSock6, &fds[nfds++], POLLIN);
1168
1169
1
    pollfd_if_valid(
1170
1
      mini_sock->ssdpSock6UlaGua, &fds[nfds++], POLLIN);
1171
1172
1
    #ifdef INCLUDE_CLIENT_APIS
1173
1
    pollfd_if_valid(mini_sock->ssdpReqSock4, &fds[nfds++], POLLIN);
1174
1175
1
    pollfd_if_valid(mini_sock->ssdpReqSock6, &fds[nfds++], POLLIN);
1176
1
    #endif /* INCLUDE_CLIENT_APIS */
1177
1178
    /* poll() */
1179
1
    ret = _poll(fds, nfds, -1);
1180
1181
1
    if (ret < 0 && errno == EINTR) {
1182
0
      continue;
1183
0
    }
1184
1185
1
    if (ret < 0) {
1186
0
      strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
1187
0
      UpnpPrintf(UPNP_CRITICAL,
1188
0
        SSDP,
1189
0
        __FILE__,
1190
0
        __LINE__,
1191
0
        "Error in poll(): %s\n",
1192
0
        errorBuffer);
1193
0
      continue;
1194
0
    }
1195
1196
1
    web_server_accept(mini_sock->miniServerSock4, &fds[1]);
1197
1
    web_server_accept(mini_sock->miniServerSock6, &fds[2]);
1198
1
    web_server_accept(mini_sock->miniServerSock6UlaGua, &fds[3]);
1199
1200
1
    #ifdef INCLUDE_CLIENT_APIS
1201
1
    ssdp_read(&mini_sock->ssdpReqSock4, &fds[7]);
1202
1
    ssdp_read(&mini_sock->ssdpReqSock6, &fds[8]);
1203
1
    #endif /* INCLUDE_CLIENT_APIS */
1204
1205
1
    ssdp_read(&mini_sock->ssdpSock4, &fds[4]);
1206
1
    ssdp_read(&mini_sock->ssdpSock6, &fds[5]);
1207
1
    ssdp_read(&mini_sock->ssdpSock6UlaGua, &fds[6]);
1208
1209
1
    stopSock = receive_from_stopSock(
1210
1
      mini_sock->miniServerStopSock, &fds[0]);
1211
1
  #endif         /* _WIN32 */
1212
1
  }
1213
1214
  /* shutdown connections */
1215
1
  shutdown_all_active_connections();
1216
1217
  /* Close all sockets. */
1218
1
  sock_close(mini_sock->miniServerSock4);
1219
1
  sock_close(mini_sock->miniServerSock6);
1220
1
  sock_close(mini_sock->miniServerSock6UlaGua);
1221
1
  sock_close(mini_sock->miniServerStopSock);
1222
1
  sock_close(mini_sock->ssdpSock4);
1223
1
  sock_close(mini_sock->ssdpSock6);
1224
1
  sock_close(mini_sock->ssdpSock6UlaGua);
1225
1
  #ifdef INCLUDE_CLIENT_APIS
1226
1
  sock_close(mini_sock->ssdpReqSock4);
1227
1
  sock_close(mini_sock->ssdpReqSock6);
1228
1
  #endif /* INCLUDE_CLIENT_APIS */
1229
1230
  /* Free mini_sock. */
1231
1
  free(mini_sock);
1232
1
  gMServState = MSERV_IDLE;
1233
1234
1
  return;
1235
1
}
1236
1237
/*!
1238
 * \brief Returns port to which socket, sockfd, is bound.
1239
 *
1240
 * \return -1 on error; check errno. 0 if successful.
1241
 */
1242
static int get_port(
1243
  /*! [in] Socket descriptor. */
1244
  SOCKET sockfd,
1245
  /*! [out] The port value if successful, otherwise, untouched. */
1246
  uint16_t *port)
1247
2
{
1248
2
  struct sockaddr_storage sockinfo;
1249
2
  socklen_t len;
1250
2
  int code;
1251
1252
2
  len = sizeof(sockinfo);
1253
2
  code = getsockname(sockfd, (struct sockaddr *)&sockinfo, &len);
1254
2
  if (code == -1) {
1255
0
    return -1;
1256
0
  }
1257
2
  if (sockinfo.ss_family == AF_INET) {
1258
2
    *port = ntohs(((struct sockaddr_in *)&sockinfo)->sin_port);
1259
2
  } else if (sockinfo.ss_family == AF_INET6) {
1260
0
    *port = ntohs(((struct sockaddr_in6 *)&sockinfo)->sin6_port);
1261
0
  }
1262
2
  UpnpPrintf(UPNP_INFO,
1263
2
    MSERV,
1264
2
    __FILE__,
1265
2
    __LINE__,
1266
2
    "sockfd = %d, .... port = %d\n",
1267
2
    sockfd,
1268
2
    (int)*port);
1269
1270
2
  return 0;
1271
2
}
1272
1273
  #ifdef INTERNAL_WEB_SERVER
1274
static int init_socket_stuff(
1275
  struct s_SocketStuff *s, const char *text_addr, int ip_version)
1276
3
{
1277
3
  char errorBuffer[ERROR_BUFFER_LEN];
1278
3
  int sockError;
1279
3
  sa_family_t domain;
1280
3
  void *addr;
1281
3
  int reuseaddr_on = MINISERVER_REUSEADDR;
1282
1283
3
  memset(s, 0, sizeof *s);
1284
3
  s->fd = INVALID_SOCKET;
1285
3
  s->ip_version = ip_version;
1286
3
  s->text_addr = text_addr;
1287
3
  s->serverAddr = (struct sockaddr *)&s->ss;
1288
3
  switch (ip_version) {
1289
1
  case 4:
1290
1
    domain = AF_INET;
1291
1
    s->serverAddr4->sin_family = domain;
1292
1
    s->address_len = sizeof *s->serverAddr4;
1293
1
    addr = &s->serverAddr4->sin_addr;
1294
1
    break;
1295
2
  case 6:
1296
2
    if (!ENABLE_IPV6) {
1297
0
      goto ok;
1298
0
    }
1299
2
    domain = AF_INET6;
1300
2
    s->serverAddr6->sin6_family = domain;
1301
2
    s->address_len = sizeof *s->serverAddr6;
1302
2
    addr = &s->serverAddr6->sin6_addr;
1303
2
    break;
1304
0
  default:
1305
0
    UpnpPrintf(UPNP_INFO,
1306
0
      MSERV,
1307
0
      __FILE__,
1308
0
      __LINE__,
1309
0
      "init_socket_stuff(): Invalid IP version: %d.\n",
1310
0
      ip_version);
1311
0
    goto error;
1312
3
  }
1313
1314
3
  if (inet_pton(domain, text_addr, addr) <= 0)
1315
2
    goto error;
1316
1317
1
  s->fd = socket(domain, SOCK_STREAM, 0);
1318
1
  if (s->fd == INVALID_SOCKET) {
1319
0
    strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
1320
0
    UpnpPrintf(UPNP_INFO,
1321
0
      MSERV,
1322
0
      __FILE__,
1323
0
      __LINE__,
1324
0
      "init_socket_stuff(): IPv%c socket not available: "
1325
0
      "%s\n",
1326
0
      ip_version,
1327
0
      errorBuffer);
1328
0
    goto error;
1329
1
  } else if (ip_version == 6) {
1330
0
    int onOff = 1;
1331
1332
0
    sockError = setsockopt(s->fd,
1333
0
      IPPROTO_IPV6,
1334
0
      IPV6_V6ONLY,
1335
0
      (char *)&onOff,
1336
0
      sizeof(onOff));
1337
0
    if (sockError == SOCKET_ERROR) {
1338
0
      strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
1339
0
      UpnpPrintf(UPNP_INFO,
1340
0
        MSERV,
1341
0
        __FILE__,
1342
0
        __LINE__,
1343
0
        "init_socket_stuff(): unable to set IPv6 "
1344
0
        "socket protocol: %s\n",
1345
0
        errorBuffer);
1346
0
      goto error;
1347
0
    }
1348
0
  }
1349
  /* Getting away with implementation of re-using address:port and
1350
   * instead choosing to increment port numbers.
1351
   * Keeping the re-use address code as an optional behaviour that
1352
   * can be turned on if necessary.
1353
   * TURN ON the reuseaddr_on option to use the option. */
1354
1
  if (MINISERVER_REUSEADDR) {
1355
1
    sockError = setsockopt(s->fd,
1356
1
      SOL_SOCKET,
1357
1
      SO_REUSEADDR,
1358
1
      (const char *)&reuseaddr_on,
1359
1
      sizeof(int));
1360
1
    if (sockError == SOCKET_ERROR) {
1361
0
      strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
1362
0
      UpnpPrintf(UPNP_INFO,
1363
0
        MSERV,
1364
0
        __FILE__,
1365
0
        __LINE__,
1366
0
        "init_socket_stuff(): unable to set "
1367
0
        "SO_REUSEADDR: %s\n",
1368
0
        errorBuffer);
1369
0
      goto error;
1370
0
    }
1371
1
  }
1372
1
ok:
1373
1
  return 0;
1374
1375
2
error:
1376
2
  if (s->fd != INVALID_SOCKET) {
1377
0
    sock_close(s->fd);
1378
0
  }
1379
2
  s->fd = INVALID_SOCKET;
1380
1381
2
  return 1;
1382
1
}
1383
1384
/*
1385
 * s->port will be one more than the used port in the end. This is important,
1386
 * in case this function is called again.
1387
 */
1388
static int do_bind(struct s_SocketStuff *s)
1389
1
{
1390
1
  int ret_val = UPNP_E_SUCCESS;
1391
1
  int bind_error;
1392
1
  int errCode = 0;
1393
1
  char errorBuffer[ERROR_BUFFER_LEN];
1394
1
  uint16_t original_listen_port = s->try_port;
1395
1396
1
  do {
1397
1
    switch (s->ip_version) {
1398
1
    case 4:
1399
1
      s->serverAddr4->sin_port = htons(s->try_port++);
1400
1
      break;
1401
0
    case 6:
1402
0
      s->serverAddr6->sin6_port = htons(s->try_port++);
1403
0
      break;
1404
1
    }
1405
1
    bind_error = bind(s->fd, s->serverAddr, s->address_len);
1406
1
    if (bind_error == SOCKET_ERROR) {
1407
    #ifdef _WIN32
1408
      errCode = WSAGetLastError();
1409
    #else  /* _WIN32 */
1410
0
      errCode = errno;
1411
0
    #endif /* _WIN32 */
1412
0
      if (errno == EADDRINUSE) {
1413
0
        errCode = 1;
1414
0
      }
1415
1
    } else {
1416
1
      errCode = 0;
1417
1
    }
1418
1
  } while (errCode != 0 && s->try_port >= original_listen_port);
1419
1
  if (bind_error == SOCKET_ERROR) {
1420
0
    strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
1421
0
    UpnpPrintf(UPNP_INFO,
1422
0
      MSERV,
1423
0
      __FILE__,
1424
0
      __LINE__,
1425
0
      "get_miniserver_sockets: "
1426
0
      "Error in IPv%d bind(): %s\n",
1427
0
      s->ip_version,
1428
0
      errorBuffer);
1429
    /* Bind failed. */
1430
0
    ret_val = UPNP_E_SOCKET_BIND;
1431
0
    goto error;
1432
0
  }
1433
1434
1
  return UPNP_E_SUCCESS;
1435
1436
0
error:
1437
0
  return ret_val;
1438
1
}
1439
1440
static int do_listen(struct s_SocketStuff *s)
1441
1
{
1442
1
  int ret_val;
1443
1
  int listen_error;
1444
1
  int port_error;
1445
1
  char errorBuffer[ERROR_BUFFER_LEN];
1446
1447
1
  listen_error = listen(s->fd, SOMAXCONN);
1448
1
  if (listen_error == SOCKET_ERROR) {
1449
0
    strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
1450
0
    UpnpPrintf(UPNP_INFO,
1451
0
      MSERV,
1452
0
      __FILE__,
1453
0
      __LINE__,
1454
0
      "do_listen(): Error in IPv%d listen(): %s\n",
1455
0
      s->ip_version,
1456
0
      errorBuffer);
1457
0
    ret_val = UPNP_E_LISTEN;
1458
0
    goto error;
1459
0
  }
1460
1
  port_error = get_port(s->fd, &s->actual_port);
1461
1
  if (port_error < 0) {
1462
0
    UpnpPrintf(UPNP_INFO,
1463
0
      MSERV,
1464
0
      __FILE__,
1465
0
      __LINE__,
1466
0
      "do_listen(): Error in get_port().\n");
1467
0
    ret_val = UPNP_E_INTERNAL_ERROR;
1468
0
    goto error;
1469
0
  }
1470
1471
1
  return UPNP_E_SUCCESS;
1472
1473
0
error:
1474
0
  return ret_val;
1475
1
}
1476
1477
static int do_reinit(struct s_SocketStuff *s)
1478
0
{
1479
0
  sock_close(s->fd);
1480
1481
0
  return init_socket_stuff(s, s->text_addr, s->ip_version);
1482
0
}
1483
1484
static int do_bind_listen(struct s_SocketStuff *s)
1485
1
{
1486
1
  int ret_val;
1487
1
  int ok = 0;
1488
1
  int original_port = s->try_port;
1489
1490
2
  while (!ok) {
1491
1
    ret_val = do_bind(s);
1492
1
    if (ret_val) {
1493
0
      goto error;
1494
0
    }
1495
1
    ret_val = do_listen(s);
1496
1
    if (ret_val) {
1497
0
      if (errno == EADDRINUSE) {
1498
0
        do_reinit(s);
1499
0
        continue;
1500
0
      }
1501
0
      goto error;
1502
0
    }
1503
1
    ok = s->try_port >= original_port;
1504
1
  }
1505
1506
1
  return 0;
1507
1508
0
error:
1509
0
  return ret_val;
1510
1
}
1511
1512
/*!
1513
 * \brief Creates a STREAM socket, binds to INADDR_ANY and listens for
1514
 * incoming connections. Returns the actual port which the sockets
1515
 * sub-system returned.
1516
 *
1517
 * Also creates a DGRAM socket, binds to the loop back address and
1518
 * returns the port allocated by the socket sub-system.
1519
 *
1520
 * \return
1521
 *  \li UPNP_E_OUTOF_SOCKET: Failed to create a socket.
1522
 *  \li UPNP_E_SOCKET_BIND: Bind() failed.
1523
 *  \li UPNP_E_LISTEN: Listen() failed.
1524
 *  \li UPNP_E_INTERNAL_ERROR: Port returned by the socket layer is < 0.
1525
 *  \li UPNP_E_SUCCESS: Success.
1526
 */
1527
static int get_miniserver_sockets(
1528
  /*! [in] Socket Array. */
1529
  MiniServerSockArray *out,
1530
  /*! [in] port on which the server is listening for incoming IPv4
1531
   * connections. */
1532
  uint16_t listen_port4,
1533
  /*! [in] port on which the server is listening for incoming IPv6
1534
   * ULA connections. */
1535
  uint16_t listen_port6,
1536
  /*! [in] port on which the server is listening for incoming
1537
   * IPv6 ULA or GUA connections. */
1538
  uint16_t listen_port6UlaGua)
1539
1
{
1540
1
  int ret_val;
1541
1
  int err_init_4;
1542
1
  int err_init_6;
1543
1
  int err_init_6UlaGua;
1544
1545
1
  struct s_SocketStuff ss4;
1546
1
  struct s_SocketStuff ss6;
1547
1
  struct s_SocketStuff ss6UlaGua;
1548
1549
  /* Create listen socket for IPv4/IPv6. An error here may indicate
1550
   * that we don't have an IPv4/IPv6 stack. */
1551
1
  err_init_4 = init_socket_stuff(&ss4, gIF_IPV4, 4);
1552
1
  err_init_6 = init_socket_stuff(&ss6, gIF_IPV6, 6);
1553
1
  err_init_6UlaGua = init_socket_stuff(&ss6UlaGua, gIF_IPV6_ULA_GUA, 6);
1554
1
  ss6.serverAddr6->sin6_scope_id = gIF_INDEX;
1555
  /* Check what happened. */
1556
1
  if (err_init_4 && (err_init_6 || err_init_6UlaGua)) {
1557
0
    UpnpPrintf(UPNP_CRITICAL,
1558
0
      MSERV,
1559
0
      __FILE__,
1560
0
      __LINE__,
1561
0
      "get_miniserver_sockets: no protocols available\n");
1562
0
    ret_val = UPNP_E_OUTOF_SOCKET;
1563
0
    goto error;
1564
0
  }
1565
  /* As per the IANA specifications for the use of ports by applications
1566
   * override the listen port passed in with the first available. */
1567
1
  if (listen_port4 == 0) {
1568
1
    listen_port4 = (uint16_t)APPLICATION_LISTENING_PORT;
1569
1
  }
1570
1
  if (listen_port6 == 0) {
1571
1
    listen_port6 = (uint16_t)APPLICATION_LISTENING_PORT;
1572
1
  }
1573
1
  if (listen_port6UlaGua < APPLICATION_LISTENING_PORT) {
1574
    /* Increment the port to make it harder to fail at first try */
1575
1
    listen_port6UlaGua = listen_port6 + 1;
1576
1
  }
1577
1
  ss4.try_port = listen_port4;
1578
1
  ss6.try_port = listen_port6;
1579
1
  ss6UlaGua.try_port = listen_port6UlaGua;
1580
1
  if (MINISERVER_REUSEADDR) {
1581
    /* THIS IS ALLOWS US TO BIND AGAIN IMMEDIATELY
1582
     * AFTER OUR SERVER HAS BEEN CLOSED
1583
     * THIS MAY CAUSE TCP TO BECOME LESS RELIABLE
1584
     * HOWEVER IT HAS BEEN SUGGESTED FOR TCP SERVERS. */
1585
1
    UpnpPrintf(UPNP_INFO,
1586
1
      MSERV,
1587
1
      __FILE__,
1588
1
      __LINE__,
1589
1
      "get_miniserver_sockets: resuseaddr is set.\n");
1590
1
  }
1591
1
  if (ss4.fd != INVALID_SOCKET) {
1592
1
    ret_val = do_bind_listen(&ss4);
1593
1
    if (ret_val) {
1594
0
      goto error;
1595
0
    }
1596
1
  }
1597
1
  if (ss6.fd != INVALID_SOCKET) {
1598
0
    ret_val = do_bind_listen(&ss6);
1599
0
    if (ret_val) {
1600
      /* IPv6 bind failed. Non-fatal when IPv4 is available
1601
       * (issue #193, #195): fall back to IPv4-only. */
1602
0
      if (ss4.fd == INVALID_SOCKET) {
1603
0
        goto error;
1604
0
      }
1605
0
      UpnpPrintf(UPNP_INFO,
1606
0
        MSERV,
1607
0
        __FILE__,
1608
0
        __LINE__,
1609
0
        "get_miniserver_sockets: IPv6 bind failed, "
1610
0
        "continuing with IPv4 only\n");
1611
0
      sock_close(ss6.fd);
1612
0
      ss6.fd = INVALID_SOCKET;
1613
0
    }
1614
0
  }
1615
1
  if (ss6UlaGua.fd != INVALID_SOCKET) {
1616
0
    ret_val = do_bind_listen(&ss6UlaGua);
1617
0
    if (ret_val) {
1618
      /* IPv6 ULA/GUA bind failed. Non-fatal when IPv4 or
1619
       * IPv6 LLA is available. */
1620
0
      if (ss4.fd == INVALID_SOCKET &&
1621
0
        ss6.fd == INVALID_SOCKET) {
1622
0
        goto error;
1623
0
      }
1624
0
      UpnpPrintf(UPNP_INFO,
1625
0
        MSERV,
1626
0
        __FILE__,
1627
0
        __LINE__,
1628
0
        "get_miniserver_sockets: IPv6 ULA/GUA bind "
1629
0
        "failed, continuing without it\n");
1630
0
      sock_close(ss6UlaGua.fd);
1631
0
      ss6UlaGua.fd = INVALID_SOCKET;
1632
0
    }
1633
0
  }
1634
1
  UpnpPrintf(UPNP_INFO,
1635
1
    MSERV,
1636
1
    __FILE__,
1637
1
    __LINE__,
1638
1
    "get_miniserver_sockets: bind successful\n");
1639
1
  out->miniServerPort4 = ss4.actual_port;
1640
1
  out->miniServerPort6 = ss6.actual_port;
1641
1
  out->miniServerPort6UlaGua = ss6UlaGua.actual_port;
1642
1
  out->miniServerSock4 = ss4.fd;
1643
1
  out->miniServerSock6 = ss6.fd;
1644
1
  out->miniServerSock6UlaGua = ss6UlaGua.fd;
1645
1646
1
  return UPNP_E_SUCCESS;
1647
1648
0
error:
1649
0
  if (ss4.fd != INVALID_SOCKET) {
1650
0
    sock_close(ss4.fd);
1651
0
  }
1652
0
  if (ss6.fd != INVALID_SOCKET) {
1653
0
    sock_close(ss6.fd);
1654
0
  }
1655
0
  if (ss6UlaGua.fd != INVALID_SOCKET) {
1656
0
    sock_close(ss6UlaGua.fd);
1657
0
  }
1658
1659
0
  return ret_val;
1660
1
}
1661
  #endif /* INTERNAL_WEB_SERVER */
1662
1663
/*!
1664
 * \brief Creates the miniserver STOP socket. This socket is created and
1665
 *  listened on to know when it is time to stop the Miniserver.
1666
 *
1667
 * \return
1668
 * \li \c UPNP_E_OUTOF_SOCKET: Failed to create a socket.
1669
 * \li \c UPNP_E_SOCKET_BIND: Bind() failed.
1670
 * \li \c UPNP_E_INTERNAL_ERROR: Port returned by the socket layer is < 0.
1671
 * \li \c UPNP_E_SUCCESS: Success.
1672
 */
1673
static int get_miniserver_stopsock(
1674
  /*! [in] Miniserver Socket Array. */
1675
  MiniServerSockArray *out)
1676
1
{
1677
1
  char errorBuffer[ERROR_BUFFER_LEN];
1678
1
  struct sockaddr_in stop_sockaddr;
1679
1
  SOCKET miniServerStopSock = 0;
1680
1
  int ret = 0;
1681
1682
1
  miniServerStopSock = socket(AF_INET, SOCK_DGRAM, 0);
1683
1
  if (miniServerStopSock == INVALID_SOCKET) {
1684
0
    strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
1685
0
    UpnpPrintf(UPNP_CRITICAL,
1686
0
      MSERV,
1687
0
      __FILE__,
1688
0
      __LINE__,
1689
0
      "Error in socket(): %s\n",
1690
0
      errorBuffer);
1691
0
    return UPNP_E_OUTOF_SOCKET;
1692
0
  }
1693
  /* Bind to local socket. */
1694
1
  memset(&stop_sockaddr, 0, sizeof(stop_sockaddr));
1695
1
  stop_sockaddr.sin_family = (sa_family_t)AF_INET;
1696
1
  inet_pton(AF_INET, "127.0.0.1", &stop_sockaddr.sin_addr);
1697
1
  ret = bind(miniServerStopSock,
1698
1
    (struct sockaddr *)&stop_sockaddr,
1699
1
    sizeof(stop_sockaddr));
1700
1
  if (ret == SOCKET_ERROR) {
1701
0
    UpnpPrintf(UPNP_CRITICAL,
1702
0
      MSERV,
1703
0
      __FILE__,
1704
0
      __LINE__,
1705
0
      "Error in binding localhost!!!\n");
1706
0
    sock_close(miniServerStopSock);
1707
0
    return UPNP_E_SOCKET_BIND;
1708
0
  }
1709
1
  ret = get_port(miniServerStopSock, &miniStopSockPort);
1710
1
  if (ret < 0) {
1711
0
    sock_close(miniServerStopSock);
1712
0
    return UPNP_E_INTERNAL_ERROR;
1713
0
  }
1714
1
  out->miniServerStopSock = miniServerStopSock;
1715
1
  out->stopPort = miniStopSockPort;
1716
1717
1
  return UPNP_E_SUCCESS;
1718
1
}
1719
1720
static UPNP_INLINE void InitMiniServerSockArray(
1721
  MiniServerSockArray *mini_socket)
1722
1
{
1723
1
  mini_socket->miniServerSock4 = INVALID_SOCKET;
1724
1
  mini_socket->miniServerSock6 = INVALID_SOCKET;
1725
1
  mini_socket->miniServerSock6UlaGua = INVALID_SOCKET;
1726
1
  mini_socket->miniServerStopSock = INVALID_SOCKET;
1727
1
  mini_socket->ssdpSock4 = INVALID_SOCKET;
1728
1
  mini_socket->ssdpSock6 = INVALID_SOCKET;
1729
1
  mini_socket->ssdpSock6UlaGua = INVALID_SOCKET;
1730
1
  mini_socket->stopPort = 0u;
1731
1
  mini_socket->miniServerPort4 = 0u;
1732
1
  mini_socket->miniServerPort6 = 0u;
1733
1
  mini_socket->miniServerPort6UlaGua = 0u;
1734
1
  #ifdef INCLUDE_CLIENT_APIS
1735
1
  mini_socket->ssdpReqSock4 = INVALID_SOCKET;
1736
1
  mini_socket->ssdpReqSock6 = INVALID_SOCKET;
1737
1
  #endif /* INCLUDE_CLIENT_APIS */
1738
1
}
1739
1740
int StartMiniServer(
1741
  /*! [in,out] Port on which the server listens for incoming IPv4
1742
   * connections. */
1743
  uint16_t *listen_port4,
1744
  /*! [in,out] Port on which the server listens for incoming IPv6
1745
   * LLA connections. */
1746
  uint16_t *listen_port6,
1747
  /*! [in,out] Port on which the server listens for incoming
1748
   * IPv6 ULA or GUA connections. */
1749
  uint16_t *listen_port6UlaGua)
1750
1
{
1751
1
  int ret_code;
1752
1
  int count;
1753
1
  int max_count = 10000;
1754
1
  MiniServerSockArray *mini_socket;
1755
1
  ThreadPoolJob job;
1756
1757
1
  memset(&job, 0, sizeof(job));
1758
1759
1
  switch (gMServState) {
1760
1
  case MSERV_IDLE:
1761
1
    break;
1762
0
  default:
1763
    /* miniserver running. */
1764
0
    return UPNP_E_INTERNAL_ERROR;
1765
1
  }
1766
1
  mini_socket =
1767
1
    (MiniServerSockArray *)malloc(sizeof(MiniServerSockArray));
1768
1
  if (!mini_socket) {
1769
0
    return UPNP_E_OUTOF_MEMORY;
1770
0
  }
1771
1
  InitMiniServerSockArray(mini_socket);
1772
1
  #ifdef INTERNAL_WEB_SERVER
1773
  /* V4 and V6 http listeners. */
1774
1
  ret_code = get_miniserver_sockets(
1775
1
    mini_socket, *listen_port4, *listen_port6, *listen_port6UlaGua);
1776
1
  if (ret_code != UPNP_E_SUCCESS) {
1777
0
    free(mini_socket);
1778
0
    return ret_code;
1779
0
  }
1780
1
  #endif /* INTERNAL_WEB_SERVER */
1781
  /* Stop socket (To end miniserver processing). */
1782
1
  ret_code = get_miniserver_stopsock(mini_socket);
1783
1
  if (ret_code != UPNP_E_SUCCESS) {
1784
0
    sock_close(mini_socket->miniServerSock4);
1785
0
    sock_close(mini_socket->miniServerSock6);
1786
0
    sock_close(mini_socket->miniServerSock6UlaGua);
1787
0
    free(mini_socket);
1788
0
    return ret_code;
1789
0
  }
1790
  /* SSDP socket for discovery/advertising. */
1791
1
  ret_code = get_ssdp_sockets(mini_socket);
1792
1
  if (ret_code != UPNP_E_SUCCESS) {
1793
0
    sock_close(mini_socket->miniServerSock4);
1794
0
    sock_close(mini_socket->miniServerSock6);
1795
0
    sock_close(mini_socket->miniServerSock6UlaGua);
1796
0
    sock_close(mini_socket->miniServerStopSock);
1797
0
    free(mini_socket);
1798
0
    return ret_code;
1799
0
  }
1800
1
  TPJobInit(&job, RunMiniServer, (void *)mini_socket);
1801
1
  TPJobSetPriority(&job, MED_PRIORITY);
1802
1
  TPJobSetFreeFunction(&job, (free_routine)free);
1803
1
  ret_code = ThreadPoolAddPersistent(&gMiniServerThreadPool, &job, NULL);
1804
1
  if (ret_code < 0) {
1805
0
    sock_close(mini_socket->miniServerSock4);
1806
0
    sock_close(mini_socket->miniServerSock6);
1807
0
    sock_close(mini_socket->miniServerSock6UlaGua);
1808
0
    sock_close(mini_socket->miniServerStopSock);
1809
0
    sock_close(mini_socket->ssdpSock4);
1810
0
    sock_close(mini_socket->ssdpSock6);
1811
0
    sock_close(mini_socket->ssdpSock6UlaGua);
1812
0
  #ifdef INCLUDE_CLIENT_APIS
1813
0
    sock_close(mini_socket->ssdpReqSock4);
1814
0
    sock_close(mini_socket->ssdpReqSock6);
1815
0
  #endif /* INCLUDE_CLIENT_APIS */
1816
0
    free(mini_socket);
1817
0
    return UPNP_E_OUTOF_MEMORY;
1818
0
  }
1819
  /* Wait for miniserver to start. */
1820
1
  count = 0;
1821
2
  while (gMServState != (MiniServerState)MSERV_RUNNING &&
1822
1
    count < max_count) {
1823
    /* 0.05s */
1824
1
    imillisleep(50);
1825
1
    count++;
1826
1
  }
1827
1
  if (count >= max_count) {
1828
    /* Took it too long to start that thread. */
1829
0
    sock_close(mini_socket->miniServerSock4);
1830
0
    sock_close(mini_socket->miniServerSock6);
1831
0
    sock_close(mini_socket->miniServerSock6UlaGua);
1832
0
    sock_close(mini_socket->miniServerStopSock);
1833
0
    sock_close(mini_socket->ssdpSock4);
1834
0
    sock_close(mini_socket->ssdpSock6);
1835
0
    sock_close(mini_socket->ssdpSock6UlaGua);
1836
0
  #ifdef INCLUDE_CLIENT_APIS
1837
0
    sock_close(mini_socket->ssdpReqSock4);
1838
0
    sock_close(mini_socket->ssdpReqSock6);
1839
0
  #endif /* INCLUDE_CLIENT_APIS */
1840
0
    return UPNP_E_INTERNAL_ERROR;
1841
0
  }
1842
1
  #ifdef INTERNAL_WEB_SERVER
1843
1
  *listen_port4 = mini_socket->miniServerPort4;
1844
1
  *listen_port6 = mini_socket->miniServerPort6;
1845
1
  *listen_port6UlaGua = mini_socket->miniServerPort6UlaGua;
1846
1
  #endif /* INTERNAL_WEB_SERVER */
1847
1848
1
  return UPNP_E_SUCCESS;
1849
1
}
1850
1851
int StopMiniServer(void)
1852
0
{
1853
0
  char errorBuffer[ERROR_BUFFER_LEN];
1854
0
  socklen_t socklen = sizeof(struct sockaddr_in);
1855
0
  SOCKET sock;
1856
0
  struct sockaddr_in ssdpAddr;
1857
0
  char buf[256] = "ShutDown";
1858
0
  size_t bufLen = strlen(buf);
1859
1860
0
  switch (gMServState) {
1861
0
  case MSERV_RUNNING:
1862
0
    gMServState = MSERV_STOPPING;
1863
0
    break;
1864
0
  default:
1865
0
    return 0;
1866
0
  }
1867
0
  sock = socket(AF_INET, SOCK_DGRAM, 0);
1868
0
  if (sock == INVALID_SOCKET) {
1869
0
    strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
1870
0
    UpnpPrintf(UPNP_INFO,
1871
0
      SSDP,
1872
0
      __FILE__,
1873
0
      __LINE__,
1874
0
      "SSDP_SERVER: StopSSDPServer: Error in socket() %s\n",
1875
0
      errorBuffer);
1876
0
    return 0;
1877
0
  }
1878
0
  while (gMServState != (MiniServerState)MSERV_IDLE) {
1879
0
    ssdpAddr.sin_family = (sa_family_t)AF_INET;
1880
0
    inet_pton(AF_INET, "127.0.0.1", &ssdpAddr.sin_addr);
1881
0
    ssdpAddr.sin_port = htons(miniStopSockPort);
1882
0
    sendto(sock,
1883
0
      buf,
1884
0
      bufLen,
1885
0
      0,
1886
0
      (struct sockaddr *)&ssdpAddr,
1887
0
      socklen);
1888
0
    imillisleep(1);
1889
0
    if (gMServState == (MiniServerState)MSERV_IDLE) {
1890
0
      break;
1891
0
    }
1892
0
    isleep(1u);
1893
0
  }
1894
0
  sock_close(sock);
1895
1896
0
  return 0;
1897
0
}
1898
#endif /* EXCLUDE_MINISERVER */