Pārlūkot izejas kodu

Отладка modbus.

TelenkovDmitry 1 gadu atpakaļ
vecāks
revīzija
4ae2ae0376
100 mainītis faili ar 36744 papildinājumiem un 31924 dzēšanām
  1. BIN
      doc/~$_модули_входов_выходов_редакция_4.doc
  2. 17 0
      fw/modules/misc/misc.c
  3. 3 0
      fw/modules/misc/misc.h
  4. 417 0
      fw/modules/modbus/modbus.c
  5. 46 0
      fw/modules/modbus/modbus.h
  6. 425 0
      fw/modules/spi_flash/spi_flash.c
  7. 53 0
      fw/modules/spi_flash/spi_flash.h
  8. 204 235
      fw/modules/usb/usb_eth.c
  9. 15 17
      fw/modules/usb/usb_eth.h
  10. 280 0
      fw/modules/user_fatfs/user_fatfs.c
  11. 29 0
      fw/modules/user_fatfs/user_fatfs.h
  12. 84 91
      fw/user/FreeRTOSConfig.h
  13. 348 348
      fw/user/lwipopts.h
  14. 25 6
      fw/user/main.c
  15. 121 116
      fw/user/usb_conf.h
  16. 1 1
      libs/artery/cmsis/cm4/device_support/system_at32f403a_407.c
  17. 3 1
      libs/artery/drivers/src/at32f403a_407_flash.c
  18. 342 342
      libs/artery/usb/inc/usb_std.h
  19. 195 195
      libs/artery/usb/inc/usbd_core.h
  20. 74 74
      libs/artery/usb/inc/usbd_int.h
  21. 74 74
      libs/artery/usb/inc/usbd_sdr.h
  22. 705 711
      libs/artery/usb/src/usbd_core.c
  23. 341 336
      libs/artery/usb/src/usbd_int.c
  24. 558 565
      libs/artery/usb/src/usbd_sdr.c
  25. BIN
      libs/thirdparty/LwIP/.DS_Store
  26. 3349 4548
      libs/thirdparty/LwIP/CHANGELOG
  27. 33 33
      libs/thirdparty/LwIP/COPYING
  28. 4 5
      libs/thirdparty/LwIP/FILES
  29. 89 0
      libs/thirdparty/LwIP/README
  30. 144 0
      libs/thirdparty/LwIP/UPGRADING
  31. 511 0
      libs/thirdparty/LwIP/doc rawapi.txt
  32. 6 9
      libs/thirdparty/LwIP/doc/FILES
  33. 63 58
      libs/thirdparty/LwIP/doc/contrib.txt
  34. 511 0
      libs/thirdparty/LwIP/doc/rawapi.txt
  35. 135 0
      libs/thirdparty/LwIP/doc/savannah.txt
  36. 181 0
      libs/thirdparty/LwIP/doc/snmp_agent.txt
  37. 267 0
      libs/thirdparty/LwIP/doc/sys_arch.txt
  38. 135 0
      libs/thirdparty/LwIP/doc90savannah.txt
  39. 267 0
      libs/thirdparty/LwIP/docpsys_arch.txt
  40. 63 0
      libs/thirdparty/LwIP/doc°contrib.txt
  41. 0 0
      libs/thirdparty/LwIP/port/FreeRTOS/bpstruct.h
  42. 0 0
      libs/thirdparty/LwIP/port/FreeRTOS/cc.h
  43. 0 0
      libs/thirdparty/LwIP/port/FreeRTOS/epstruct.h
  44. 9 6
      libs/thirdparty/LwIP/port/FreeRTOS/ethernetif.c
  45. 0 0
      libs/thirdparty/LwIP/port/FreeRTOS/ethernetif.h
  46. 0 0
      libs/thirdparty/LwIP/port/FreeRTOS/perf.h
  47. 0 0
      libs/thirdparty/LwIP/port/FreeRTOS/sys_arch.c
  48. 489 0
      libs/thirdparty/LwIP/port/Standalone/ethernetif.c
  49. 11 0
      libs/thirdparty/LwIP/port/Standalone/ethernetif.h
  50. BIN
      libs/thirdparty/LwIP/src/.DS_Store
  51. 13 15
      libs/thirdparty/LwIP/src/FILES
  52. 995 1367
      libs/thirdparty/LwIP/src/api/api_lib.c
  53. 1947 2173
      libs/thirdparty/LwIP/src/api/api_msg.c
  54. 115 115
      libs/thirdparty/LwIP/src/api/err.c
  55. 246 250
      libs/thirdparty/LwIP/src/api/netbuf.c
  56. 413 414
      libs/thirdparty/LwIP/src/api/netdb.c
  57. 221 380
      libs/thirdparty/LwIP/src/api/netifapi.c
  58. 2828 0
      libs/thirdparty/LwIP/src/api/sockets.c
  59. 518 658
      libs/thirdparty/LwIP/src/api/tcpip.c
  60. 179 174
      libs/thirdparty/LwIP/src/apps/httpd/fs.c
  61. 21 21
      libs/thirdparty/LwIP/src/apps/httpd/fs/404.html
  62. 0 0
      libs/thirdparty/LwIP/src/apps/httpd/fs/img/sics.gif
  63. 47 47
      libs/thirdparty/LwIP/src/apps/httpd/fs/index.html
  64. 50 41
      libs/thirdparty/LwIP/src/apps/httpd/fsdata.h
  65. 2634 2746
      libs/thirdparty/LwIP/src/apps/httpd/httpd.c
  66. 114 0
      libs/thirdparty/LwIP/src/apps/httpd/httpd_structs.h
  67. 97 97
      libs/thirdparty/LwIP/src/apps/httpd/makefsdata/makefsdata
  68. 1033 1251
      libs/thirdparty/LwIP/src/apps/httpd/makefsdata/makefsdata.c
  69. 13 13
      libs/thirdparty/LwIP/src/apps/httpd/makefsdata/readme.txt
  70. 661 0
      libs/thirdparty/LwIP/src/apps/lwiperf/lwiperf.c
  71. 2028 2407
      libs/thirdparty/LwIP/src/apps/mdns/mdns.c
  72. 1373 1463
      libs/thirdparty/LwIP/src/apps/mqtt/mqtt.c
  73. 367 0
      libs/thirdparty/LwIP/src/apps/netbiosns/netbiosns.c
  74. 749 704
      libs/thirdparty/LwIP/src/apps/snmp/snmp_asn1.c
  75. 108 113
      libs/thirdparty/LwIP/src/apps/snmp/snmp_asn1.h
  76. 1349 1353
      libs/thirdparty/LwIP/src/apps/snmp/snmp_core.c
  77. 76 83
      libs/thirdparty/LwIP/src/apps/snmp/snmp_core_priv.h
  78. 116 116
      libs/thirdparty/LwIP/src/apps/snmp/snmp_mib2.c
  79. 182 182
      libs/thirdparty/LwIP/src/apps/snmp/snmp_mib2_icmp.c
  80. 375 368
      libs/thirdparty/LwIP/src/apps/snmp/snmp_mib2_interfaces.c
  81. 743 731
      libs/thirdparty/LwIP/src/apps/snmp/snmp_mib2_ip.c
  82. 227 227
      libs/thirdparty/LwIP/src/apps/snmp/snmp_mib2_snmp.c
  83. 377 380
      libs/thirdparty/LwIP/src/apps/snmp/snmp_mib2_system.c
  84. 594 607
      libs/thirdparty/LwIP/src/apps/snmp/snmp_mib2_tcp.c
  85. 357 372
      libs/thirdparty/LwIP/src/apps/snmp/snmp_mib2_udp.c
  86. 1668 1955
      libs/thirdparty/LwIP/src/apps/snmp/snmp_msg.c
  87. 194 185
      libs/thirdparty/LwIP/src/apps/snmp/snmp_msg.h
  88. 121 123
      libs/thirdparty/LwIP/src/apps/snmp/snmp_netconn.c
  89. 156 156
      libs/thirdparty/LwIP/src/apps/snmp/snmp_pbuf_stream.c
  90. 73 72
      libs/thirdparty/LwIP/src/apps/snmp/snmp_pbuf_stream.h
  91. 100 115
      libs/thirdparty/LwIP/src/apps/snmp/snmp_raw.c
  92. 220 232
      libs/thirdparty/LwIP/src/apps/snmp/snmp_scalar.c
  93. 343 342
      libs/thirdparty/LwIP/src/apps/snmp/snmp_table.c
  94. 224 231
      libs/thirdparty/LwIP/src/apps/snmp/snmp_threadsync.c
  95. 447 458
      libs/thirdparty/LwIP/src/apps/snmp/snmp_traps.c
  96. 136 136
      libs/thirdparty/LwIP/src/apps/snmp/snmpv3.c
  97. 145 0
      libs/thirdparty/LwIP/src/apps/snmp/snmpv3_dummy.c
  98. 331 342
      libs/thirdparty/LwIP/src/apps/snmp/snmpv3_mbedtls.c
  99. 66 69
      libs/thirdparty/LwIP/src/apps/snmp/snmpv3_priv.h
  100. 727 869
      libs/thirdparty/LwIP/src/apps/sntp/sntp.c

BIN
doc/~$_модули_входов_выходов_редакция_4.doc


+ 17 - 0
fw/modules/misc/misc.c

@@ -3,6 +3,8 @@
 #include "FreeRTOS.h"
 #include "FreeRTOS.h"
 #include "task.h"
 #include "task.h"
 #include "mux.h"
 #include "mux.h"
+#include "mb.h"
+#include "mbport.h"
 #include <stdio.h>
 #include <stdio.h>
 
 
 
 
@@ -31,6 +33,21 @@ void modbus_tim_init(void)
 }
 }
 
 
 
 
+//
+void modbus_init(void)
+{
+    const UCHAR ucSlaveID[] = {0xAA, 0xBB, 0xCC};
+    uint32_t baud = 115200;
+    eMBParity par = MB_PAR_NONE;
+    UCHAR mb_addr = 1;
+    unsigned int stop_bits = 1;
+#if 1    
+    eMBInit(MB_RTU, mb_addr, 4, baud, par, stop_bits);
+    eMBSetSlaveID(0x34, TRUE, ucSlaveID, 3);
+	eMBEnable();
+#endif    
+}
+
 
 
 #if 0
 #if 0
 //
 //

+ 3 - 0
fw/modules/misc/misc.h

@@ -5,6 +5,9 @@
 //
 //
 void modbus_tim_init(void);
 void modbus_tim_init(void);
 
 
+//
+void modbus_init(void);
+
 
 
 #endif  // __MISC_H
 #endif  // __MISC_H
 
 

+ 417 - 0
fw/modules/modbus/modbus.c

@@ -0,0 +1,417 @@
+#include "at32f403a_407.h"
+#include "modbus.h"
+//#include "modbus_params.h"
+#include "FreeRTOS.h"
+#include "task.h"
+//#include "settings.h"
+#include "mb.h"
+#include "mbport.h"
+//#include "common_settings.h"
+//#include "sys_api.h"
+//#include "pid_hal.h"
+#include "mbrtu.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+
+#define REG_HOLDING_START		( 1 )
+#define REG_HOLDING_NREGS		( 5 )
+
+
+
+static USHORT	usRegHoldingStart = REG_HOLDING_START;
+extern UCHAR    rcvAddress;
+
+//osThreadId modbus_task_handle;
+static void modbus_task(void *params);
+//osThreadId modbus_params_handle;
+static void modbus_params(void *params);
+//osTimerId reset_timer_handle;
+//osTimerId settings_timer_handle;
+//osTimerId modbus_timer_handle;
+
+mb_delay_action_t mb_action = MB_NO_ACTION;
+uint8_t new_slave_addr;
+bool mb_package_flag = false;
+
+
+
+//
+void mb_init(void)
+{
+    uint32_t baud = 115200;
+    eMBParity par = MB_PAR_NONE;
+    UCHAR mb_addr = 1;
+    unsigned int stop_bits = 1;
+	const UCHAR ucSlaveID[] = {0xAA, 0xBB, 0xCC};
+    
+	//mb_init_params();
+
+    eMBInit(MB_RTU, mb_addr, 4, baud, par, stop_bits);
+	eMBSetSlaveID(0x34, TRUE, ucSlaveID, 3);
+	eMBEnable();
+    
+    
+    xTaskCreate(modbus_task, "modbus_task", 2*configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL);
+    
+    xTaskCreate(modbus_params, "modbus_params", 4*configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL);
+    
+#if 0    
+	osTimerDef(vResetTimer, modbus_reset);
+	reset_timer_handle = osTimerCreate(osTimer(vResetTimer), osTimerOnce, NULL);
+    
+    osTimerDef(vSettingsTimer, settings_timer);
+    settings_timer_handle = osTimerCreate(osTimer(vSettingsTimer), osTimerOnce, NULL);
+    
+    osTimerDef(vModbusTimer, modbus_port_timer);
+    modbus_timer_handle = osTimerCreate(osTimer(vModbusTimer), osTimerOnce, NULL);
+#endif    
+}
+
+//
+void modbus_task(void *params)
+{
+	(void)params;
+
+	for (;;)
+	{
+		eMBPoll();
+	}
+
+}
+
+//
+void modbus_params(void *params)
+{
+    for (;;)
+    {
+        if (mb_action != MB_NO_ACTION)
+        {
+            switch (mb_action)
+            {
+                // Установка нового адреса
+                case MB_CHANGE_ADDR :
+    
+                    //settings.com_settings.mb_addr = new_slave_addr;  
+                    eMBSetSlaveAddr(new_slave_addr);
+                      
+                break;
+                
+                case MB_CHANGE_PORT_SETTINGS :
+#if 0                  
+                    osDelay(200);
+                    eMBRTUStop();
+                    settings_set_modbus_params(temp_modbus_port);
+                    settings_init_mb_port(settings.com_settings.mb_addr);
+                    MBDBG settings_print_modbus();
+                    eMBRTUStart();
+                    eMBEnable();
+                    mb_package_flag = false;
+                    osTimerStart(modbus_timer_handle, 9800);
+#endif                  
+                break;
+                
+                // Полное восстановление дефолтных настроек
+                case MB_DEF_SETTINGS :
+#if 0
+                    settings_set_default(true);
+                    settings_save();
+#endif                  
+                break;  
+                
+                // Частичное восстановление дефолтных настроек
+                case MB_PART_DEF_SETTINGS :
+#if 0
+                    settings_set_default(false);
+                    settings_save();
+#endif                  
+                break;
+                
+                  
+                case MB_SAVE_SETTINGS :
+#if 0                
+                    settings_save();
+#endif                  
+                break;
+                
+                case MB_SAVE_SYS_SETTINGS :
+#if 0
+                    if (set_sys_settings_flag) {
+                        memcpy(&sys_settings, &temp_sys_settings, sizeof(sys_settings));
+                        sys_settings_save();
+                    }
+#endif                  
+                break;
+                
+                case MB_RESET :
+                  
+                    vTaskDelay(100);
+                    NVIC_SystemReset();
+                  
+                break;
+                
+                default : break;
+            }
+            
+            mb_action = MB_NO_ACTION;
+        }
+        vTaskDelay(10);
+    }
+}
+
+
+// Отложенная перезагрузка в Bootloader
+void modbus_reset(void const * params)
+{
+#if 0  
+	MBDBG printf("Switch to bootloader!\r\n");
+	rtc_set_load_mode(1);
+	HAL_NVIC_SystemReset();
+#endif    
+}
+
+
+// Запуск таймера на изменение системных настроек
+void settings_timer(void const * params)
+{
+#if 0  
+    set_sys_settings_flag = false;
+    psw_ok = false;
+#endif    
+}
+
+
+// Запуск таймера для изменение настроек modbus
+void modbus_port_timer(void const * params)
+{
+#if 0
+    eMBRTUStop();
+  
+    // Были транзакции по modbus с новыми настройками. 
+    // Нужно сохранить новые настройки.
+    if (mb_package_flag)
+    {
+        settings.com_settings.mb_port = temp_modbus_port;
+        settings_set_modbus_params(settings.com_settings.mb_port);
+        settings_init_mb_port(settings.com_settings.mb_addr);
+        settings_save();
+        
+    }
+    // Нужно вернуть старые настройки
+    else
+    {
+        temp_modbus_port = settings.com_settings.mb_port;
+        settings_set_modbus_params(settings.com_settings.mb_port);
+        settings_init_mb_port(settings.com_settings.mb_addr);
+    }
+  
+    
+    MBDBG settings_print_modbus();
+    eMBRTUStart();
+    eMBEnable();
+#endif    
+}
+
+// ------------------------------------------------------------------- //
+//						Modbus callback's
+// ------------------------------------------------------------------- //
+
+//
+// 65 (0x41) Read Input Registers	 
+eMBErrorCode
+eMBUpdateCB( UCHAR * pucFrame, USHORT * usLen)
+{
+	eMBErrorCode    eStatus = MB_ENOERR;
+#if 0	
+    mb_package_flag = true;
+    
+	// Команда перехода в bootloader.
+	if (pucFrame[1] == 0x01)
+	{
+		osTimerStart(reset_timer_handle, 100);
+	}
+#endif
+    return eStatus;
+}
+
+eMBErrorCode
+eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
+{
+	return MB_ENOREG;
+}
+
+
+// 03 (0x03) Read Holding Registers
+// чтение N регистров управления и уставок
+// 16 (0x10) Write Multiple registers
+// запись N регистров управления и уставок (0x10)
+eMBErrorCode
+eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode )
+{
+	eMBErrorCode eStatus = MB_ENOERR;
+
+	int			 iRegIndex;
+    
+    mb_delay_action_t mb_act = MB_NO_ACTION;
+	uint16_t regs_cnt = usNRegs;
+	uint16_t index_param;
+	uint8_t *ptr_buf = pucRegBuffer;
+	uint16_t size;
+
+	printf("usAddress: 0x%X\r\n", usAddress);
+	printf("usNRegs: %u\r\n", usNRegs);
+	printf("eMode: %u\r\n", eMode);
+    printf("pucRegBuffer :%X\r\n", pucRegBuffer[0]);
+    
+    mb_package_flag = true;
+    
+	iRegIndex = (int)(usAddress - usRegHoldingStart);
+
+#if 0    
+	switch (eMode)
+	{
+		case MB_REG_READ :
+
+			while (regs_cnt)
+			{
+				// Поиск регистра
+				if (!mb_find_param(iRegIndex, &index_param, &size))
+				{
+					return MB_ENOREG; // нет запрашиваемого регистра
+				}
+				else
+				{
+					MBDBG printf("reg: %X, index: %u, size: %u\r\n", iRegIndex, index_param, size);
+                    MBDBG printf("reg: %X\r\n", iRegIndex);
+					mb_get_param(ptr_buf,  index_param); // Вызов функции записи
+					iRegIndex += size;
+					ptr_buf += 2*size;
+					regs_cnt -= size;
+				}
+			}
+
+
+		break;
+
+		case MB_REG_WRITE :
+
+			while (regs_cnt)
+			{
+				// Поиск регистра
+				if (!mb_find_param(iRegIndex, &index_param, &size))
+				{
+					return MB_ENOREG; // нет запрашиваемого регистра
+				}
+				else
+				{
+					MBDBG printf("reg: %X, index: %u, size: %u\r\n", iRegIndex, index_param, size);
+					mb_act = mb_set_param(ptr_buf, index_param); // Вызов функции записи
+
+                    if (mb_act != MB_NO_ACTION)
+                        mb_action = mb_act;
+                    
+					iRegIndex += size;
+					ptr_buf += 2*size;
+					regs_cnt -= size;
+                    
+                    if (mb_act == MB_PAS_ERR)
+                        return MB_EINVAL;
+				}
+			}
+
+		break;
+	}
+#endif
+	return eStatus;
+}
+
+// 17 (0x11) Report Slave ID (Serial Line only) 
+// чтение информации об устройстве (0x11)
+eMBException
+eMBFuncReportSlaveID( UCHAR * pucFrame, USHORT * usLen )
+{
+#if 0
+    mb_package_flag = true;
+    
+	*usLen = mb_get_info(&pucFrame[1]) + 3;
+#endif
+	return MB_EX_NONE;
+}
+
+
+// чтение/запись значений из нескольких регистров флагов (Coil Status).
+// чтения N регистров параметров
+eMBErrorCode
+eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode )
+{
+    return MB_ENOREG;
+}
+
+eMBErrorCode
+eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
+{
+    return MB_ENOREG;
+}
+
+
+// 0x42
+// Запрос на изменение адреса
+// Если запрос широковещательный, то нужно проверить присланный ID.
+eMBException    
+eMBSetAddrIdCB( UCHAR * pucFrame, USHORT * usLen )
+{
+#if 0  
+    mb_package_flag = true;
+    
+    if (rcvAddress == MB_ADDRESS_BROADCAST)
+    {
+        if (mb_set_addr_id(&pucFrame[1], &new_slave_addr))
+            mb_action = MB_CHANGE_ADDR;
+    }
+    // Адресный запрос. ID не проверяем
+    else
+    {
+        if (mb_set_addr(&pucFrame[1], &new_slave_addr))
+        {
+            pucFrame[1] = new_slave_addr;
+            mb_action = MB_CHANGE_ADDR;
+        }
+        else 
+            pucFrame[1] = MB_BOOT_ERR_WRONG_CONTENT;
+    }
+    
+    *usLen = 2;
+#endif    
+    return MB_EX_NONE;
+}
+
+
+// 0x43
+eMBException
+eMBSetAddrSerialCB( UCHAR * pucFrame, USHORT * usLen )
+{
+#if 0  
+    mb_package_flag = true;
+      
+    if (rcvAddress == MB_ADDRESS_BROADCAST)
+    {
+        if (mb_set_addr_serial(&pucFrame[1], &new_slave_addr))
+            mb_action = MB_CHANGE_ADDR;
+    }
+    // Адресный запрос. ID не проверяем
+    else
+    {
+        if (mb_set_addr(&pucFrame[1], &new_slave_addr))
+        {
+            pucFrame[1] = new_slave_addr;
+            mb_action = MB_CHANGE_ADDR;
+        }
+        else 
+            pucFrame[1] = MB_BOOT_ERR_WRONG_CONTENT;
+    }
+    
+    *usLen = 2;
+#endif    
+    return MB_EX_NONE;
+}
+

+ 46 - 0
fw/modules/modbus/modbus.h

@@ -0,0 +1,46 @@
+#ifndef __MODBUS_H
+#define __MODBUS_H
+
+
+//#define MB__DEBUG
+
+#ifdef MB__DEBUG
+#define MBDBG
+#else
+#define MBDBG if (0)
+#endif
+
+
+//
+typedef enum 
+{
+    MB_NO_ACTION = 0,
+    MB_CHANGE_ADDR,
+    MB_CHANGE_PORT_SETTINGS,
+    MB_DEF_SETTINGS,
+    MB_PART_DEF_SETTINGS,
+    MB_SAVE_SETTINGS,
+    MB_SAVE_SYS_SETTINGS,
+    MB_CHANGE_PWM_FREQ_1,
+    MB_CHANGE_PWM_FREQ_2,
+    MB_CHANGE_PWM_FREQ_3,
+    MB_RESET,
+    MB_PAS_OK,
+    MB_PAS_ERR
+  
+} mb_delay_action_t ;
+
+
+//
+void mb_init(void);
+
+//
+void modbus_reset(void const * params);
+
+//
+void settings_timer(void const * params);
+
+//
+void modbus_port_timer(void const * params);
+
+#endif // __MODBUS_H

+ 425 - 0
fw/modules/spi_flash/spi_flash.c

@@ -0,0 +1,425 @@
+#include "at32f403a_407.h"
+#include "spi_flash.h"
+#include "spi_common.h"
+#include "FreeRTOS.h"
+#include "task.h"
+#include "common_config.h"
+#include <string.h>
+#include <stdio.h>
+
+#define SPI_FLASH			SPI3
+
+
+
+
+#define INCLUDE_SPI_TEST	0
+
+//SemaphoreHandle_t spi_mutex;
+
+static uint8_t spi_tx_rx(uint8_t byte) {
+    return common_spi_tx_rx(SPI_FLASH, byte);
+}
+
+
+#define CMD_WREN	0x06 // +
+#define CMD_WRDI	0x04 // +
+#define CMD_WRSR	0x01 // +
+#define CMD_RDSR	0x05 // +
+#if defined (FLASH_TYPE_AT25SF161)  
+#define CMD_READ	0x03 //0x0B // +
+#elif defined (FLASH_TYPE_MX25L1606E)
+#define CMD_READ	0x03 // +
+#endif
+#define CMD_SE		0x20 // + Block Erse 4 Kbytes 
+#define CMD_BE		0x52 // Block Erase 32 Kbytes 
+#define CMD_CE		0x60 // + Chip Erase
+#define CMD_PP		0x02 // + Byte/Page Program (1 to 256 Bytes)
+#define CMD_RDSFDP	0x5A // - not used for FLASH_TYPE_AT25SF161
+
+#define SR_WIP	(1 << 0)
+#define SR_WEL	(1 << 1)
+#define SR_SRWD	(1 << 7)
+
+static spi_flash_desc_t spi_flash_desc;
+
+static inline void wait_write_enable(void) {
+	uint8_t status;
+	SPI_FLASH_CS_L();
+	spi_tx_rx(CMD_RDSR);
+	do {
+		status = spi_tx_rx(0);
+	} while (!(status & SR_WEL));
+	SPI_FLASH_CS_H();
+}
+
+static inline void wait_write_end(void) {
+	uint8_t status;
+	SPI_FLASH_CS_L();
+	spi_tx_rx(CMD_RDSR);
+	do {
+		status = spi_tx_rx(0);
+	} while (status & SR_WIP);
+	SPI_FLASH_CS_H();
+}
+
+static inline void send_addr(int addr) {
+	spi_tx_rx((addr >> 16) & 0xFF);
+	spi_tx_rx((addr >> 8) & 0xFF);
+	spi_tx_rx(addr & 0xFF);
+}
+
+static int spi_flash_read_sfdp(int addr, void *buf, size_t len) {
+    uint8_t *ptr = (uint8_t*)buf;
+	//xSemaphoreTake(spi_mutex, portMAX_DELAY);
+	SPI_FLASH_CS_L();
+	spi_tx_rx(CMD_RDSFDP);
+	send_addr(addr);
+	spi_tx_rx(0);
+	while (len--)
+        *ptr++ = spi_tx_rx(0);
+	SPI_FLASH_CS_H();
+	//xSemaphoreGive(spi_mutex);
+	return 0;
+}
+
+ssize_t spi_flash_read(int addr, void *buf, size_t len, uint32_t timeout) {
+    uint8_t *ptr = (uint8_t*)buf;
+	(void)timeout;
+	//xSemaphoreTake(spi_mutex, portMAX_DELAY);
+	SPI_FLASH_CS_L();
+	spi_tx_rx(CMD_READ);
+	send_addr(addr);
+	while (len--)
+        *ptr++ = spi_tx_rx(0);
+	SPI_FLASH_CS_H();
+	//xSemaphoreGive(spi_mutex);
+	return len;
+}
+
+#define TIMEOUT 10000
+
+uint16_t spi_flash_pp(int addr, const void *buf, size_t len, uint32_t timeout) {
+    uint8_t *ptr = (uint8_t*)buf;
+	(void)timeout;
+	ssize_t ret = 0;
+
+	if ((addr & 0xFF) + len > 0xFF)
+	    len = 0x100 - (addr & 0xFF);
+	ret = len;
+	//xSemaphoreTake(spi_mutex, portMAX_DELAY);
+	SPI_FLASH_CS_L();
+	spi_tx_rx(CMD_WREN);
+	SPI_FLASH_CS_H();
+
+	wait_write_enable();
+
+	SPI_FLASH_CS_L();
+	spi_tx_rx(CMD_PP);
+	send_addr(addr);
+	while (len--)
+        spi_tx_rx(*ptr++);
+	SPI_FLASH_CS_H();
+
+	wait_write_end();
+	//xSemaphoreGive(spi_mutex);
+	return ret;
+}
+
+ssize_t spi_flash_write(int addr, const void *buf, size_t len, uint32_t timeout) {
+	(void)timeout;
+	int ret = 0, offset = 0;
+	do {
+        ret = spi_flash_pp(addr + offset, (uint8_t*)buf + offset, len - offset, 0);
+	    offset += ret;
+	} while (len - offset);
+
+	return 0;
+}
+
+int spi_flash_chip_erase(uint32_t timeout) {
+	(void)timeout;
+	//xSemaphoreTake(spi_mutex, portMAX_DELAY);
+	SPI_FLASH_CS_L();
+	spi_tx_rx(CMD_WREN);
+	SPI_FLASH_CS_H();
+	wait_write_enable();
+
+	SPI_FLASH_CS_L();
+	spi_tx_rx(CMD_CE);
+	SPI_FLASH_CS_H();
+
+	wait_write_end();
+	//xSemaphoreGive(spi_mutex);
+	return 0;
+}
+
+int spi_flash_erase_sector(int addr, uint32_t timeout) {
+	(void)timeout;
+ 	//xSemaphoreTake(spi_mutex, portMAX_DELAY);
+	SPI_FLASH_CS_L();
+	spi_tx_rx(CMD_WREN);
+	SPI_FLASH_CS_H();
+
+	wait_write_enable();
+
+	SPI_FLASH_CS_L();
+	spi_tx_rx(CMD_SE);
+	send_addr(addr);
+	SPI_FLASH_CS_H();
+
+	wait_write_end();
+ 	//xSemaphoreGive(spi_mutex);
+	return 0;
+}
+
+unsigned int spi_flash_get_sector_size(void) {
+	return spi_flash_desc.sector_size;
+}
+
+unsigned int spi_flash_get_sector_count(void) {
+	return spi_flash_desc.sector_count;
+}
+
+unsigned int spi_flash_get_total_size(void) {
+	return spi_flash_desc.sector_count * spi_flash_desc.sector_size;
+}
+
+bool spi_flash_is_init(void) {
+	return spi_flash_desc.present;
+}
+
+
+bool spi_flash_init(void) 
+{
+    bool ret = false;
+    uint8_t tmp[4] = {0};
+    
+    SPI_FLASH_CS_H();
+  
+    spi_flash_get_id(tmp);
+    if (!(tmp[0] == 0x1F && tmp[1] == 0x86 && tmp[2] == 0x01))
+    {
+        ret = false;
+        
+        spi_flash_read_sfdp(0, tmp, 4);
+        if (!(tmp[0] == 0x53 && tmp[1] == 0x46 && tmp[2] == 0x44 && tmp[3] == 0x50))
+            ret = false;
+        else 
+            ret = true;
+    }
+    else 
+        ret = true;
+      
+    if (ret)
+    {
+        spi_flash_desc.sector_size = SPI_FLASH_SECTOR_SIZE;
+        spi_flash_desc.sector_erase_op = 32;
+        spi_flash_desc.sector_count = 512;
+        spi_flash_desc.present = true;
+        return true;
+    }
+    else    
+        return false;
+  
+#if 0  
+#if defined (FLASH_TYPE_AT25SF161)  
+    
+    uint8_t tmp[4] = {0};
+    
+    SPI_FLASH_CS_H();
+  
+    spi_flash_get_id(tmp);
+    if (!(tmp[0] == 0x1F && tmp[1] == 0x86 && tmp[2] == 0x01))
+		return 0;
+    
+   	spi_flash_desc.sector_size = SPI_FLASH_SECTOR_SIZE;
+	spi_flash_desc.sector_erase_op = 32;
+	spi_flash_desc.sector_count = 512;
+	spi_flash_desc.present = true;
+
+    
+    return 1;
+    
+#elif defined (FLASH_TYPE_MX25L1606E)
+  
+	uint32_t i, ptable, bitsize = 0;
+	uint8_t tmp[4];
+
+	spi_flash_desc.present = false;
+
+	// check SFDP magic
+    
+    SPI_FLASH_CS_H();
+    
+	spi_flash_read_sfdp(0, tmp, 4);
+	if (!(tmp[0] == 0x53 && tmp[1] == 0x46 && 
+		tmp[2] == 0x44 && tmp[3] == 0x50))
+		return 0;
+
+	// get parameter headers count
+	spi_flash_read_sfdp(0x06, tmp, 1);
+
+	// find first jedec pheader (with ID == 0)
+	for (ptable = 0x08, i = 0; i <= tmp[0]; i++, ptable += 8) {
+		spi_flash_read_sfdp(ptable, tmp, 1);
+		if (tmp[0] == 0)
+			break;
+	}
+
+	// read ptable pointer from pheader
+	spi_flash_read_sfdp(ptable + 4, &ptable, 3);
+
+	// get flash density (size in bits)
+	if (spi_flash_read_sfdp(ptable + 4, &bitsize, 4) < 0 || !bitsize)
+		return 0;
+
+	// find smallest available sector
+	for (i = 0; i < 4; i++) {
+		tmp[0] = 0;
+		if (spi_flash_read_sfdp(ptable + 0x1C + i*2, &tmp, 2) >= 0 &&
+				tmp[0]) {
+			spi_flash_desc.sector_size = 1 << tmp[0];
+			spi_flash_desc.sector_erase_op = tmp[1];
+			spi_flash_desc.sector_count = (bitsize + 1) >> (3 + tmp[0]);
+			break;
+		}
+	}
+	if (!spi_flash_desc.sector_size)
+		return 0;
+
+	spi_flash_desc.present = true;
+    
+	return 1;
+    
+#endif
+    return 0;
+#endif    
+}
+
+
+/*
+bool spi_flash_init(void){
+	//spi_init_();
+	spi_flash_desc.sector_size = SPI_FLASH_SECTOR_SIZE;
+	spi_flash_desc.sector_erase_op = 32;
+	spi_flash_desc.sector_count = 512;
+	spi_flash_desc.present = true;
+
+	return 1;
+}
+*/
+
+/*
+void init_spi_mutex(void)
+{
+	spi_mutex = xSemaphoreCreateMutex();
+}
+*/
+
+// -------------------------------------------------------------------------- //
+
+void spi_flash_get_id(uint8_t *buf)
+{
+ 	//xSemaphoreTake(spi_mutex, portMAX_DELAY);
+	SPI_FLASH_CS_L();
+	spi_tx_rx(0x9F);
+    *buf++ = spi_tx_rx(0);
+    *buf++ = spi_tx_rx(0);
+    *buf++ = spi_tx_rx(0);
+    
+	SPI_FLASH_CS_H();
+	//xSemaphoreGive(spi_mutex);
+}
+
+// -------------------------------------------------------------------------- //
+
+
+
+#if INCLUDE_SPI_TEST
+const uint8_t txbuf1[] = "This film came out on DVD yesterday and I rushed to buy it. \
+		    This version is the first to render all the detail and perfection \
+		    of Jack Cardiff's amazing compositions and brilliant, varied photography. \
+		    As a collection of memorable images, this film is better than any comparable \
+		    historical epic of the period and even gives GWTW a run for its money. \
+		    King Vidor's direction is a series of 'tableaux vivants' where the \
+		    characters are not posing but acting in a very natural, period-specific way. \
+		    I have never had a problem with this adaptation of Tolstoy's novel. \
+		    I think it is a wonderful introduction to the period and the novel and that \
+		    it is a very poetic, very original work in its own right. Henry Fonda's \
+		    characterization is especially moving, including great memorable interactions \
+		    with/reations to Mel Ferrer, Audrey Hepburn, Helmut Dantine and John Mills, but \
+		    all members of the cast are actually perfect. The harrowing last 45 minutes of \
+		    the film manage to convey a sense of history, a sense of grandeur as well as to \
+		    communicate very clearly Tolstoy's ideas about the meaning of life, by relying \
+		    mostly on the power of memorable images. The most conspicuous handicap of this movie, \
+		    in my opinion, is its soundtrack (in glorious mono). The barely hi-fi recording of \
+		    dialogues and music sounds pinched, hollow and tinny and it always has in very version \
+		    I have ever seen: in the theatres, on TV and on video. Even the soundtrack album is \
+		    an atrocity. In some scenes, before the necessary adjustments of bass and treble, \
+		    Audrey Hepburn's and Mel Ferrer's voices actually hurt your ear. Nino Rota's very \
+		    Russian-sounding score is serviceable and melodic, although rather sparse in its \
+		    orchestration and in the number of players. One can only wonder what 'War and Peace' \
+		    could have sounded like with a cohort of Hollywood arrangers, decent recording facilities \
+		    and lavish, varied orchestrations in true high fidelity and stereophonic sound. \
+		    According to Lukas Kendall of 'Film Score Monthly', the original recording elements \
+		    of the soundtrack have long ago disappeared, which is the common lot of international, \
+		    independent co-productions of the era. Someone somewhere is certainly guilty of \
+		    skimping on quality or embezzlement for this 1956 movie to sound so much worse \
+		    than a 1939, pre-hi-fi epic like GWTW. Like all VistaVision films, this one was \
+		    meant to be shown in Perspecta Stereophonic Sound where the mono dialog track was \
+		    meant to be channelled to three different directions, making it directional, while \
+		    the separate mono music + sound effects track was generally directed to all three \
+		    speakers at the same time. The results fooled the viewers into thinking everything \
+		    was in true stereo and the reproduction of the music was usually in very high fidelity. \
+		    Maybe the soundtrack used on the DVD is a mono reduction of those two separate tracks \
+		    that has squandered that fidelity and maybe the DVD can be issued again with better \
+		    results in some kind of 4.0 presentation. When they do, very little electronic \
+		    restoration work will be needed to make the image absolutely perfect. \
+		    But let's concentrate on the positive: This film is a summit of visual \
+		    splendour and its sets, costumes, colour photography, composition and lighting \
+            achieve, in every single scene, wonders of artistry, creativity and delicacy \
+		    But let's concentrate on the positive: This film is a summit of visual \
+		    splendour and its sets, costumes, colour photography, composition and lighting \
+		    achieve, in every single scene, wonders of artistry, creativity and delicacy \
+		    that will probably never be equalled. Suffice it to say that it has, \
+		    among many other treasures, a sunrise duel scene in the snow that still has viewers \
+		    wondering whether it was shot outdoors or in a studio and that will have them wondering foreverhsgkhgkshgu.\r\n";
+
+
+#define countof(a) (sizeof(a) / sizeof(*(a)))
+#define  bufsize1 (countof(txbuf1)-1)
+
+uint8_t  rxbuf1[bufsize1] = {0};
+
+bool spi_flash_test(void) {
+    if (!spi_flash_is_init()) {
+		return false;
+    }
+	
+	const unsigned int sector_count = spi_flash_get_sector_count();
+	const unsigned int sector_size = spi_flash_get_sector_size();
+	const unsigned int total_size = spi_flash_get_total_size();
+
+	printf("Present: %u sectors, %u bytes per sector (%u bytes total)\r\n",
+            sector_count, sector_size, total_size);
+
+    for (uint16_t i = 0; i < sector_count; i++) {
+		printf("sector: %u/%u ", i, sector_count);
+
+		spi_flash_erase_sector(i * sector_size, 0);
+		spi_flash_write(i * sector_size, txbuf1, bufsize1, 0);
+
+		memset(rxbuf1, 0, bufsize1);
+		spi_flash_read(i * sector_size, rxbuf1, bufsize1, 0);
+
+		if (memcmp(txbuf1, rxbuf1, bufsize1) != 0) {
+			printf("fail\r\n");
+			return false;
+		}
+		printf("ok\r\n");
+    }
+	printf("\r\nDone\r\n");	
+
+    return true;
+}
+#endif

+ 53 - 0
fw/modules/spi_flash/spi_flash.h

@@ -0,0 +1,53 @@
+#ifndef SPI_FLASH_H
+#define SPI_FLASH_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+
+#if defined ( __ICCARM__ )
+#define ssize_t long
+#else
+#include <unistd.h>
+#endif
+
+
+#include "FreeRTOS.h"
+#include "task.h"
+#include "semphr.h"
+
+#define SPI_FLASH_SECTOR_SIZE		        4096
+#define SPI_FLASH_SECTORS_IN_BLOCK_NUMBER   16
+#define SPI_FLASH_BLOCK_SIZE                16
+#define SPI_FLASH_BLOCK_NUMBER		        32
+
+//extern SemaphoreHandle_t spi_mutex;
+
+typedef struct {
+	bool present;
+	uint32_t sector_size;
+	uint8_t sector_erase_op;
+	uint16_t sector_count;
+} spi_flash_desc_t;
+
+extern bool spi_flash_init(void);
+extern ssize_t spi_flash_read(int addr, void *buf, size_t len, uint32_t timeout);
+extern ssize_t spi_flash_write(int addr, const void *buf, size_t len, uint32_t timeout);
+extern int spi_flash_erase_sector(int addr, uint32_t timeout);
+extern int spi_flash_chip_erase(uint32_t timeout);
+extern unsigned int spi_flash_get_sector_size(void);
+extern unsigned int spi_flash_get_sector_count(void);
+extern unsigned int spi_flash_get_total_size(void);
+bool spi_flash_is_init(void);
+bool spi_flash_test(void);
+
+//void init_spi_mutex(void);
+
+
+// -------------------------------------------------------------------------- //
+
+void spi_flash_get_id(uint8_t *buf);
+
+// -------------------------------------------------------------------------- //
+
+#endif /* SPI_FLASH_H */

+ 204 - 235
fw/modules/usb/usb_eth.c

@@ -1,235 +1,204 @@
-#include "at32f403a_407.h"
-#include "usb_eth.h"
-#include <stdbool.h>
-#include "dhserver.h"
-#include "dnserver.h"
-#include <stdlib.h>
-#ifdef PRINTF_STDLIB
-#include <stdio.h>
-#endif
-#ifdef PRINTF_CUSTOM
-#include "tinystdio.h"
-#endif
-#include "usbd_rndis_core.h"
-//#include "usbd_usr.h"
-//#include "usbd_desc.h"
-//#include "usb_conf.h"
-#include "netif/etharp.h"
-#include "lwip/init.h"
-#include "lwip/netif.h"
-#include "lwip/pbuf.h"
-#include "lwip/icmp.h"
-#include "lwip/udp.h"
-#include "lwip/opt.h"
-#include "lwip/arch.h"
-#include "lwip/api.h"
-#include "lwip/inet.h"
-#include "lwip/dns.h"
-#include "tcpip.h"
-//#include "time.h"
-//#include "usb_core.h"
-#include "usbd_core.h"
-#include "usb.h"
-#include "common.h"
-//#include "board.h"
-
-
-#include "FreeRTOS.h"
-#include "task.h"
-#include "semphr.h"
-
-
-static uint8_t hwaddr[6]  = {0x20,0x89,0x84,0x6A,0x96,00};
-static uint8_t ipaddr[4]  = {192, 168, 7, 1};
-static uint8_t netmask[4] = {255, 255, 255, 0};
-static uint8_t gateway[4] = {0, 0, 0, 0};
-
-#define USE_LINK_LED	0
-
-#define NUM_DHCP_ENTRY 3
-
-static dhcp_entry_t entries[NUM_DHCP_ENTRY] =
-{
-	// mac    ip address        subnet mask        lease time
-	{ {0}, {192, 168, 7, 2}, {255, 255, 255, 0}, 24 * 60 * 60 },
-	{ {0}, {192, 168, 7, 3}, {255, 255, 255, 0}, 24 * 60 * 60 },
-	{ {0}, {192, 168, 7, 4}, {255, 255, 255, 0}, 24 * 60 * 60 }
-};
-
-static dhcp_config_t dhcp_config =
-{
-	{192, 168, 7, 1}, 67, // server address, port
-	{192, 168, 7, 1},     // dns server
-	"prs",                // dns suffix
-	NUM_DHCP_ENTRY,       // num entry
-	entries               // entries
-};
-
-struct netif netif_data;
-
-
-static uint8_t received[ETH_MTU + 14];
-static int recvSize = 0;
-
-SemaphoreHandle_t xSemUsbEth;
-
-
-void on_packet(const char *data, int size)
-{
-	BaseType_t xHigherPriorityTaskWoken = pdFALSE;;
-
-	memcpy(received, data, size);
-	recvSize = size;
-	xSemaphoreGiveFromISR( xSemUsbEth, &xHigherPriorityTaskWoken );
-
-	if( xHigherPriorityTaskWoken != pdFALSE )
-	{
-		//vPortYield();
-        taskYIELD();
-	}
-}
-
-void usb_polling()
-{
-	xSemaphoreTake( xSemUsbEth, portMAX_DELAY);
-
-	if (recvSize == 0) 
-		return;
-
-	struct pbuf *frame;
-	frame = pbuf_alloc(PBUF_RAW, recvSize, PBUF_POOL);
-
-	if (frame == NULL) 
-		return;
-
-	memcpy(frame->payload, received, recvSize);
-	frame->len = recvSize;
-	recvSize = 0;
-
-	tcpip_input(frame, &netif_data);
-
-	pbuf_free(frame);
-}
-
-static int outputs = 0;
-
-err_t output_fn(struct netif *netif, struct pbuf *p, ip_addr_t *ipaddr)
-{
-	return etharp_output(netif, p, ipaddr);
-}
-
-err_t linkoutput_fn(struct netif *netif, struct pbuf *p)
-{
-	int i;
-	struct pbuf *q;
-	static char data[ETH_MTU + 14 + 4];
-	int size = 0;
-	
-	for (i = 0; i < 200; i++)
-	{
-		if (rndis_can_send()) break;
-		vTaskDelay(1);
-	}
-
-  	for(q = p; q != NULL; q = q->next)
-	{
-		if (size + q->len > ETH_MTU + 14)
-			return ERR_ARG;
-		memcpy(data + size, (char *)q->payload, q->len);
-		size += q->len;
-	}
-
-	if (!rndis_can_send())
-		return ERR_USE;
-
-	rndis_send(data, size);
-	outputs++;
-	return ERR_OK;
-}
-
-err_t netif_init_cb(struct netif *netif)
-{
-	LWIP_ASSERT("netif != NULL", (netif != NULL));
-	netif->mtu = ETH_MTU;
-	netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP | NETIF_FLAG_UP;
-	netif->state = NULL;
-	netif->name[0] = 'E';
-	netif->name[1] = 'X';
-//	netif->dhcp = NULL;
-	netif->linkoutput = linkoutput_fn;
-	//netif->output = output_fn;
-    netif->output = (netif_output_fn)output_fn;
-    //err_t output_fn(struct netif *netif, struct pbuf *p, ip_addr_t *ipaddr)
-    //typedef err_t (*netif_output_fn)(struct netif *netif, struct pbuf *p, const ip4_addr_t *ipaddr);
-	return ERR_OK;
-}
-
-#define PADDR(ptr) ((ip_addr_t *)ptr)
-
-void init_lwip()
-{
-	struct netif  *netif = &netif_data;
-
-	netif->hwaddr_len = 6;
-	memcpy(netif->hwaddr, hwaddr, 6);
-
-	netif = netif_add(netif, PADDR(ipaddr), PADDR(netmask), PADDR(gateway), NULL, netif_init_cb, tcpip_input);
-	netif_set_link_up(netif);
-	netif_set_up(netif);
-}
-
-void usb_hw_init(void)
-{
-	usb_init();
-	rndis_rxproc = on_packet;
-
-	xSemUsbEth = xSemaphoreCreateBinary();
-}
-
-bool dns_query_proc(const char *name, ip_addr_t *addr)
-{
-	if (strcmp(name, "energomera.prs") == 0 || strcmp(name, "www.energomera.prs") == 0)
-	{
-		addr->addr = *(uint32_t *)ipaddr;
-		return true;
-	}
-	return false;
-}
-
-void usb_eth_task(void *arg)
-{
-    while (1)
-    {
-        usb_polling();     // usb device polling
-    }
-}
-
-void usb_eth_init(void)
-{
-	usb_hw_init();
-
-	//while (rndis_state != rndis_initialized) ;
- 
-	init_lwip();
-
-	while (!netif_is_up(&netif_data)) ;
-
-	while (dhserv_init(&dhcp_config) != ERR_OK) ;
-
-	while (dnserv_init(PADDR(ipaddr), 53, dns_query_proc) != ERR_OK) ;
-
-	//xTaskCreate(usb_eth_task, ( char * ) "usb_eth_task", 2*configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL);
-    xTaskCreate(usb_eth_task, ( char * ) "usb_eth_task", 3*configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL);
-}
-
-#ifdef USE_USB_OTG_FS
-void OTG_FS_WKUP_IRQHandler(void)
-{
-  if(USB_OTG_dev.cfg.low_power)
-  {
-    *(uint32_t *)(0xE000ED10) &= 0xFFFFFFF9 ;
-    USB_OTG_UngateClock(&USB_OTG_dev);
-  }
-  EXTI_ClearITPendingBit(EXTI_Line18);
-}
-#endif
+#include "at32f403a_407.h"
+#include "usb_eth.h"
+#include "dhserver.h"
+#include "dnserver.h"
+#include "usbd_rndis_core.h"
+#include "netif/etharp.h"
+#include "lwip/init.h"
+#include "lwip/netif.h"
+#include "lwip/pbuf.h"
+#include "lwip/icmp.h"
+#include "lwip/udp.h"
+#include "lwip/opt.h"
+#include "lwip/arch.h"
+#include "lwip/api.h"
+#include "lwip/inet.h"
+#include "lwip/dns.h"
+#include "tcpip.h"
+#include "usbd_core.h"
+#include "usb.h"
+#include "common.h"
+#include "FreeRTOS.h"
+#include "task.h"
+#include "semphr.h"
+#include <stdbool.h>
+#include <stdlib.h>
+
+
+
+static uint8_t hwaddr[6]  = {0x20,0x89,0x84,0x6A,0x96,00};
+static uint8_t ipaddr[4]  = {192, 168, 7, 1};
+static uint8_t netmask[4] = {255, 255, 255, 0};
+static uint8_t gateway[4] = {0, 0, 0, 0};
+
+#define USE_LINK_LED	0
+
+#define NUM_DHCP_ENTRY 3
+
+static dhcp_entry_t entries[NUM_DHCP_ENTRY] =
+{
+	// mac    ip address        subnet mask        lease time
+	{ {0}, {192, 168, 7, 2}, {255, 255, 255, 0}, 24 * 60 * 60 },
+	{ {0}, {192, 168, 7, 3}, {255, 255, 255, 0}, 24 * 60 * 60 },
+	{ {0}, {192, 168, 7, 4}, {255, 255, 255, 0}, 24 * 60 * 60 }
+};
+
+static dhcp_config_t dhcp_config =
+{
+	{192, 168, 7, 1}, 67, // server address, port
+	{192, 168, 7, 1},     // dns server
+	"prs",                // dns suffix
+	NUM_DHCP_ENTRY,       // num entry
+	entries               // entries
+};
+
+struct netif netif_data;
+
+
+static uint8_t received[RNDIS_MTU + 14];
+static int recvSize = 0;
+
+SemaphoreHandle_t xSemUsbEth;
+
+
+void on_packet(const char *data, int size)
+{
+	BaseType_t xHigherPriorityTaskWoken = pdFALSE;;
+
+	memcpy(received, data, size);
+	recvSize = size;
+	xSemaphoreGiveFromISR( xSemUsbEth, &xHigherPriorityTaskWoken );
+
+	if( xHigherPriorityTaskWoken != pdFALSE )
+	{
+        taskYIELD();
+	}
+}
+
+void usb_polling()
+{
+	xSemaphoreTake( xSemUsbEth, portMAX_DELAY);
+
+	if (recvSize == 0) 
+		return;
+
+	struct pbuf *frame;
+	frame = pbuf_alloc(PBUF_RAW, recvSize, PBUF_POOL);
+
+	if (frame == NULL) 
+		return;
+
+	memcpy(frame->payload, received, recvSize);
+	frame->len = recvSize;
+	recvSize = 0;
+
+	tcpip_input(frame, &netif_data);
+
+	pbuf_free(frame);
+}
+
+static int outputs = 0;
+
+err_t output_fn(struct netif *netif, struct pbuf *p, ip_addr_t *ipaddr)
+{
+	return etharp_output(netif, p, ipaddr);
+}
+
+err_t linkoutput_fn(struct netif *netif, struct pbuf *p)
+{
+	int i;
+	struct pbuf *q;
+	static char data[RNDIS_MTU + 14 + 4];
+	int size = 0;
+	
+	for (i = 0; i < 200; i++)
+	{
+		if (rndis_can_send()) break;
+		vTaskDelay(1);
+	}
+
+  	for(q = p; q != NULL; q = q->next)
+	{
+		if (size + q->len > RNDIS_MTU + 14)
+			return ERR_ARG;
+		memcpy(data + size, (char *)q->payload, q->len);
+		size += q->len;
+	}
+
+	if (!rndis_can_send())
+		return ERR_USE;
+
+	rndis_send(data, size);
+	outputs++;
+	return ERR_OK;
+}
+
+err_t netif_init_cb(struct netif *netif)
+{
+	LWIP_ASSERT("netif != NULL", (netif != NULL));
+	netif->mtu = RNDIS_MTU;
+	netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP | NETIF_FLAG_UP;
+	netif->state = NULL;
+	netif->name[0] = 'E';
+	netif->name[1] = 'X';
+	netif->linkoutput = linkoutput_fn;
+    netif->output = (netif_output_fn)output_fn;
+	return ERR_OK;
+}
+
+#define PADDR(ptr) ((ip_addr_t *)ptr)
+
+void init_lwip()
+{
+	struct netif  *netif = &netif_data;
+
+	netif->hwaddr_len = 6;
+	memcpy(netif->hwaddr, hwaddr, 6);
+
+	netif = netif_add(netif, PADDR(ipaddr), PADDR(netmask), PADDR(gateway), NULL, netif_init_cb, tcpip_input);
+	netif_set_link_up(netif);
+	netif_set_up(netif);
+}
+
+void usb_hw_init(void)
+{
+	usb_init();
+	rndis_rxproc = on_packet;
+
+	xSemUsbEth = xSemaphoreCreateBinary();
+}
+
+bool dns_query_proc(const char *name, ip_addr_t *addr)
+{
+	if (strcmp(name, "energomera.prs") == 0 || strcmp(name, "www.energomera.prs") == 0)
+	{
+		addr->addr = *(uint32_t *)ipaddr;
+		return true;
+	}
+	return false;
+}
+
+void usb_eth_task(void *arg)
+{
+    while (1)
+    {
+        usb_polling();     // usb device polling
+    }
+}
+
+void usb_eth_init(void)
+{
+	usb_hw_init();
+
+    tcpip_init(NULL, NULL);
+    
+	init_lwip();
+
+	while (!netif_is_up(&netif_data)) ;
+
+	while (dhserv_init(&dhcp_config) != ERR_OK) ;
+
+	while (dnserv_init(PADDR(ipaddr), 53, dns_query_proc) != ERR_OK) ;
+	
+    xTaskCreate(usb_eth_task, ( char * ) "usb_eth_task", 3*configMINIMAL_STACK_SIZE, NULL, 2, NULL);
+}

+ 15 - 17
fw/modules/usb/usb_eth.h

@@ -1,17 +1,15 @@
-#ifndef USB_ETH_H_
-#define USB_ETH_H_
-
-#include <stdbool.h>
-
-
-//
-void usb_switch(bool state);
-
-
-extern struct netif netif_data;
-
-void usb_eth_init(void);
-
-
-
-#endif /* USB_ETH_H_ */
+#ifndef USB_ETH_H_
+#define USB_ETH_H_
+
+#include <stdbool.h>
+
+
+extern struct netif netif_data;
+
+
+//
+void usb_eth_init(void);
+
+
+
+#endif /* USB_ETH_H_ */

+ 280 - 0
fw/modules/user_fatfs/user_fatfs.c

@@ -0,0 +1,280 @@
+#include "at32f403a_407.h"
+#include "user_fatfs.h"
+#include "common_config.h"
+#include "common.h"
+#include "FreeRTOS.h"
+#include "task.h"
+#include "semphr.h"
+#include "spi_flash.h"
+#include <string.h>
+#include <stdlib.h>
+
+#ifdef PRINTF_STDLIB
+#include <stdio.h>
+#endif
+#ifdef PRINTF_CUSTOM
+#include "tinystdio.h"
+#endif
+
+#define MAX_DRV_NUM 2  // Текущее число поддерживаемых дисков
+
+SemaphoreHandle_t spiFlashMutex;
+
+FIL update_file;  // Файл прошивки
+
+FATFS *disks[MAX_DRV_NUM];
+
+static unsigned long writePtr[MAX_DRV_NUM] = {0, 0};  // Указатель на позицию для записи в файл
+
+static uint32_t strCount = 0;  // Число строк в файле
+static uint32_t pageCount = 0; // Число страниц в файле
+char file_path[32];
+
+static bool fSecDrvEn = false;  // Флаг присутствия второстепенного диска
+static bool fPrimDrvEn = false;  // Флаг присутствия основного диска
+static bool fatfs_init = false;
+
+
+//
+bool InitFS(BYTE drv) 
+{
+    FRESULT  res;
+    FATFS   *disk;
+    DWORD free;
+    uint32_t space;
+    
+    spiFlashMutex = xSemaphoreCreateMutex();
+
+    snprintf(file_path, sizeof(file_path), "%u:", drv);
+
+    disk = pvPortMalloc(sizeof(FATFS));
+    if (disk == NULL) {
+        printf("DRIVE %u: malloc error\n\r", drv);
+        fatfs_init = true;
+        return false;
+    }
+    
+    // Монтировать диск
+    res = f_mount(disk, "", 1);
+   
+    // Создать файловую систему
+    if (res != FR_OK) {
+        res = f_mkfs(file_path,0,0);
+    }
+
+    f_getfree("/", &free, &disk);
+    space = free * disk->ssize;
+    
+    if (space == 0) {
+        f_mkfs(file_path,0,0);
+        f_getfree("/", &free, &disk);
+        space = free * disk->ssize;
+    }
+
+    res = f_chdir(file_path);
+
+    if (res == FR_NO_FILESYSTEM) 
+    {
+        printf("DRIVE %u: no filesystem found\n\r", drv);
+
+        switch(drv) 
+        {
+            case SPI_FLASH:
+                spi_flash_erase_sector(0,0);
+                spi_flash_erase_sector(1,0);
+                spi_flash_erase_sector(2,0);
+                spi_flash_erase_sector(3,0);
+            break;
+        }
+        f_mkfs(file_path,0,0);
+
+        if (f_chdir(file_path) != FR_OK) {
+            printf("DRIVE %u: filesystem error\n\r", drv);
+            vPortFree(disk);
+            fatfs_init = true;
+            return false;
+        }
+        else {
+            printf("DRIVE %u: filesystem created successfully\n\r", drv);
+        }
+    }
+    else if (res == FR_NOT_READY) {
+        printf("DRIVE %u: not ready\n\r", drv);
+        printf("DRIVE %u: File system is not initialized. Error!\n\r", drv);
+        vPortFree(disk);
+        fatfs_init = true;
+        return false;
+    }
+    else if(res == FR_OK) 
+    {
+        // Проверка файла лога
+        res = f_open(&update_file, LOG_NAME, FA_READ); 
+        if (res != FR_OK) {
+            res = f_open(&update_file, LOG_NAME, FA_WRITE | FA_CREATE_NEW); 
+            if (res != FR_OK) {
+                f_close(&update_file);
+                printf("DRIVE %u: File system is not initialized. Error!\n\r", drv);
+                fatfs_init = true;
+                return false;
+            }
+        }
+        f_close(&update_file);
+    }
+
+    printf("DRIVE %u: filesystem initialized successfully\n\r", drv);
+    fatfs_init = true;
+    return true;
+}
+
+/**
+  * @brief  Возвращает число файлов в папке
+  * @param *path имя папки в виде "/LOG"
+  * @retval 
+  */
+uint32_t GetFilesCount(char *path)
+{
+    FRESULT res;
+    FILINFO fno;
+    DIR dir;
+    char *fn;
+    uint32_t fileCount;
+  
+    res = f_opendir(&dir, path);
+  
+    if (res != FR_OK)
+        return 0;  // Не удалось открыть директорию
+  
+    for (;;)
+    {
+        res = f_readdir(&dir, &fno); // Чтение объекта директории 
+    
+	    // Останов цикла при ошибке или при достижении конца списка директории 
+	    if (res != FR_OK || fno.fname[0] == 0)
+            return fileCount;
+            
+	    if (fno.fname[0] == '.')
+            continue; // Игнорирование элемента 'точка' 
+    
+        fn = fno.fname;
+
+        if (!(fno.fattrib & AM_DIR)) 
+        {   // Это директория 
+            fileCount++;
+	        FSDBG printf("%s\r\n",fn);
+        }
+        else
+        {
+            FSDBG printf("%s/%s\r\n", path, fn);
+        }
+  }
+}
+
+/* Get fs file raw data */
+uint32_t GetFileData(BYTE drv, char *file_name, unsigned long ptr, char *str, uint32_t size)
+{
+     unsigned long readPtr;
+     FRESULT res;
+     uint32_t count = 0;
+
+     res = f_open(&update_file, file_name, FA_WRITE); 
+     if (res != FR_OK) {
+         f_close(&update_file);
+         return 0;
+     }
+
+     readPtr = ptr;
+
+     res = f_lseek(&update_file, readPtr);
+     if (res != FR_OK) {
+         f_close(&update_file);
+         return 0;
+     }
+
+     res = f_read(&update_file, str, size, (UINT *)&count);
+     if (res != FR_OK) {
+         f_close(&update_file);
+         return 0;
+     }
+     f_close(&update_file);
+
+     return count;
+}
+
+/* Put fs file raw data */
+uint32_t PutFileData(BYTE drv, char *file_name, unsigned long ptr, char *str, uint32_t size, bool truncate)
+{
+    FRESULT res;
+    uint32_t count = 0;
+    
+    if (xSemaphoreTake(spiFlashMutex, 2000) != pdTRUE) 
+        return 0;
+
+    res = f_open(&update_file, file_name, FA_WRITE); 
+    if (res != FR_OK) {
+        f_close(&update_file);
+        xSemaphoreGive(spiFlashMutex);
+        return 0;
+    }
+    if( ptr == 1 ){
+      res = f_lseek(&update_file, 0);
+    }
+    else{
+      res = f_lseek(&update_file, update_file.fsize);
+    }
+    if (res != FR_OK) {
+        f_close(&update_file);
+        xSemaphoreGive(spiFlashMutex);
+        return 0;
+    }
+
+    res = f_write(&update_file, str, size, (UINT *)&count);
+    if (res != FR_OK) {
+        f_close(&update_file);
+        xSemaphoreGive(spiFlashMutex);
+        return 0;
+    }
+
+    if (truncate) {
+        res = f_truncate(&update_file);
+        if (res != FR_OK) {
+            xSemaphoreGive(spiFlashMutex);
+        }
+    }
+    
+    f_close(&update_file);
+
+    xSemaphoreGive(spiFlashMutex);
+    
+    return count;
+}
+
+//
+void EraseFlash()
+{
+    if (xSemaphoreTake(spiFlashMutex, 10000) != pdTRUE) 
+        return;
+
+    spi_flash_erase_sector(0,0);
+    spi_flash_erase_sector(1,0);
+    spi_flash_erase_sector(2,0);
+    spi_flash_erase_sector(3,0);
+
+    xSemaphoreGive(spiFlashMutex);
+}
+
+//
+void flash_check_space(FATFS *fs)
+{
+    DWORD fre_clust, fre_sect;
+    
+    // Получение информации о томе и количество свободных кластеров
+    f_getfree(0, &fre_clust, &fs);
+    fre_sect = fre_clust * fs->csize;
+    //availableSpace = fre_sect/2;
+}
+
+//
+bool fatfs_isinit(void)
+{
+    return fatfs_init;
+}

+ 29 - 0
fw/modules/user_fatfs/user_fatfs.h

@@ -0,0 +1,29 @@
+#ifndef USER_FATFS_H
+#define USER_FATFS_H
+
+#include <stdbool.h>
+#include "diskio.h"
+#include "ff.h"
+
+#define PRIM_DRIVE        SPI_FLASH // Основной диск для хранения файлов Лога, прошивки, скриптов
+#define SEC_DRIVE         USB       // Второстепенный диск для хранения файла прошивки
+
+bool InitFS(BYTE drv);
+
+/* Get fs file raw data */
+uint32_t PutFileData(BYTE drv, char *file_name, unsigned long ptr, char *str, uint32_t size, bool truncate);
+
+/* Get fs file raw data */
+uint32_t GetFileData(BYTE drv, char *file_name, unsigned long ptr, char *str, uint32_t size);
+
+//
+void EraseFlash();
+
+//
+void flash_check_space(FATFS *fs);
+
+//
+bool fatfs_isinit(void);
+
+
+#endif

+ 84 - 91
fw/user/FreeRTOSConfig.h

@@ -1,5 +1,5 @@
 /*
 /*
-    FreeRTOS V10.4.3 - Copyright (C) 2020 Real Time Engineers Ltd.
+    FreeRTOS V8.2.0 - Copyright (C) 2015 Real Time Engineers Ltd.
     All rights reserved
     All rights reserved
 
 
     VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION.
     VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION.
@@ -8,14 +8,14 @@
 
 
     FreeRTOS is free software; you can redistribute it and/or modify it under
     FreeRTOS is free software; you can redistribute it and/or modify it under
     the terms of the GNU General Public License (version 2) as published by the
     the terms of the GNU General Public License (version 2) as published by the
-    Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception.
+    Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception.
 
 
-    ***************************************************************************
+	***************************************************************************
     >>!   NOTE: The modification to the GPL is included to allow you to     !<<
     >>!   NOTE: The modification to the GPL is included to allow you to     !<<
     >>!   distribute a combined work that includes FreeRTOS without being   !<<
     >>!   distribute a combined work that includes FreeRTOS without being   !<<
     >>!   obliged to provide the source code for proprietary components     !<<
     >>!   obliged to provide the source code for proprietary components     !<<
     >>!   outside of the FreeRTOS kernel.                                   !<<
     >>!   outside of the FreeRTOS kernel.                                   !<<
-    ***************************************************************************
+	***************************************************************************
 
 
     FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY
     FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY
     WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
     WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
@@ -37,17 +37,17 @@
     ***************************************************************************
     ***************************************************************************
 
 
     http://www.FreeRTOS.org/FAQHelp.html - Having a problem?  Start by reading
     http://www.FreeRTOS.org/FAQHelp.html - Having a problem?  Start by reading
-    the FAQ page "My application does not run, what could be wrong?".  Have you
-    defined configASSERT()?
+	the FAQ page "My application does not run, what could be wrong?".  Have you
+	defined configASSERT()?
 
 
-    http://www.FreeRTOS.org/support - In return for receiving this top quality
-    embedded software for free we request you assist our global community by
-    participating in the support forum.
+	http://www.FreeRTOS.org/support - In return for receiving this top quality
+	embedded software for free we request you assist our global community by
+	participating in the support forum.
 
 
-    http://www.FreeRTOS.org/training - Investing in training allows your team to
-    be as productive as possible as early as possible.  Now you can receive
-    FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers
-    Ltd, and the world's leading authority on the world's leading RTOS.
+	http://www.FreeRTOS.org/training - Investing in training allows your team to
+	be as productive as possible as early as possible.  Now you can receive
+	FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers
+	Ltd, and the world's leading authority on the world's leading RTOS.
 
 
     http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products,
     http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products,
     including FreeRTOS+Trace - an indispensable productivity tool, a DOS
     including FreeRTOS+Trace - an indispensable productivity tool, a DOS
@@ -65,39 +65,12 @@
     mission critical applications that require provable dependability.
     mission critical applications that require provable dependability.
 
 
     1 tab == 4 spaces!
     1 tab == 4 spaces!
-  *                       Copyright notice & Disclaimer
-  *
-  * The software Board Support Package (BSP) that is made available to
-  * download from Artery official website is the copyrighted work of Artery.
-  * Artery authorizes customers to use, copy, and distribute the BSP
-  * software and its related documentation for the purpose of design and
-  * development in conjunction with Artery microcontrollers. Use of the
-  * software is governed by this copyright notice and the following disclaimer.
-  *
-  * THIS SOFTWARE IS PROVIDED ON "AS IS" BASIS WITHOUT WARRANTIES,
-  * GUARANTEES OR REPRESENTATIONS OF ANY KIND. ARTERY EXPRESSLY DISCLAIMS,
-  * TO THE FULLEST EXTENT PERMITTED BY LAW, ALL EXPRESS, IMPLIED OR
-  * STATUTORY OR OTHER WARRANTIES, GUARANTEES OR REPRESENTATIONS,
-  * INCLUDING BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
-  * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
-  *
-  **************************************************************************
 */
 */
 
 
+
 #ifndef FREERTOS_CONFIG_H
 #ifndef FREERTOS_CONFIG_H
 #define FREERTOS_CONFIG_H
 #define FREERTOS_CONFIG_H
 
 
-#ifdef __cplusplus
-    extern "C" {
-#endif
-
-#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
-    #include <stdint.h>
-    #include "system_at32f403a_407.h"
-#endif
-
-
-
 /*-----------------------------------------------------------
 /*-----------------------------------------------------------
  * Application specific definitions.
  * Application specific definitions.
  *
  *
@@ -109,73 +82,93 @@
  *
  *
  * See http://www.freertos.org/a00110.html.
  * See http://www.freertos.org/a00110.html.
  *----------------------------------------------------------*/
  *----------------------------------------------------------*/
-#define configUSE_PREEMPTION                1
-#define configUSE_IDLE_HOOK                 0
-#define configUSE_TICK_HOOK                 0
-#define configCPU_CLOCK_HZ                  ( ( unsigned long ) system_core_clock )
-#define configTICK_RATE_HZ                  ( ( TickType_t ) 1000 )
-#define configMAX_PRIORITIES                ( 5 )
-#define configMINIMAL_STACK_SIZE            ( ( unsigned short ) 128 )
-#define configSUPPORT_DYNAMIC_ALLOCATION    1
-#define configTOTAL_HEAP_SIZE               ( ( size_t ) ( 64 * 1024 ) )
-#define configMAX_TASK_NAME_LEN             ( 16 )
-#define configUSE_16_BIT_TICKS              0
-#define configIDLE_SHOULD_YIELD             1
 
 
+/* Use a guard to ensure the following few definitions are'nt included in
+assembly files that include this header file. */
+#ifndef __IASMARM__
+	#include <stdint.h>
+	extern unsigned int system_core_clock;
+//    extern volatile unsigned long ulHighFrequencyTimerTicks;
+#endif
+
+#define configENABLE_BACKWARD_COMPATIBILITY 1
+
+#define configUSE_PREEMPTION			1
+#define configUSE_IDLE_HOOK				0
+#define configUSE_TICK_HOOK				0
+#define configCPU_CLOCK_HZ				( system_core_clock )
+#define configTICK_RATE_HZ				( ( TickType_t ) 1000 )
+#define configMAX_PRIORITIES			( 5 )
+#define configMINIMAL_STACK_SIZE		( ( unsigned short ) 128 )
+#define configTOTAL_HEAP_SIZE			( ( size_t ) ( 64 * 1024 ) )
+#define configMAX_TASK_NAME_LEN			( 16 )
+#define configUSE_TRACE_FACILITY		1
+#define configUSE_16_BIT_TICKS			0
+#define configIDLE_SHOULD_YIELD			1
+#define configUSE_MUTEXES				1
+#define configQUEUE_REGISTRY_SIZE		8
+#define configGENERATE_RUN_TIME_STATS	0
+#define configCHECK_FOR_STACK_OVERFLOW	2
+#define configUSE_RECURSIVE_MUTEXES		1
+#define configUSE_MALLOC_FAILED_HOOK	1
+#define configUSE_APPLICATION_TASK_TAG	0
+#define configUSE_COUNTING_SEMAPHORES	1
+#define configUSE_STATS_FORMATTING_FUNCTIONS	1
+#define configAPPLICATION_ALLOCATED_HEAP        0
+    
+//#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() ( ulHighFrequencyTimerTicks = 0UL )
+//#define portGET_RUN_TIME_COUNTER_VALUE()         ulHighFrequencyTimerTicks    
 
 
 /* Co-routine definitions. */
 /* Co-routine definitions. */
-#define configUSE_CO_ROUTINES               0
-#define configMAX_CO_ROUTINE_PRIORITIES     ( 2 )
+#define configUSE_CO_ROUTINES 		0
+#define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
+
+/* Software timer definitions. */
+#define configUSE_TIMERS				1
+#define configTIMER_TASK_PRIORITY		( 2 )
+#define configTIMER_QUEUE_LENGTH		10
+#define configTIMER_TASK_STACK_DEPTH	( configMINIMAL_STACK_SIZE * 2 )
 
 
 /* Set the following definitions to 1 to include the API function, or zero
 /* Set the following definitions to 1 to include the API function, or zero
 to exclude the API function. */
 to exclude the API function. */
-
-#define INCLUDE_vTaskPrioritySet            1
-#define INCLUDE_uxTaskPriorityGet           1
-#define INCLUDE_vTaskDelete                 1
-#define INCLUDE_vTaskCleanUpResources       1
-#define INCLUDE_vTaskSuspend                1
-#define INCLUDE_vTaskDelayUntil             1
-#define INCLUDE_vTaskDelay                  1
-#define INCLUDE_xTaskGetCurrentTaskHandle   1
-/* Cortex-M specific definitions. */
+#define INCLUDE_vTaskPrioritySet		1
+#define INCLUDE_uxTaskPriorityGet		1
+#define INCLUDE_vTaskDelete				1
+#define INCLUDE_vTaskCleanUpResources	1
+#define INCLUDE_vTaskSuspend			1
+#define INCLUDE_vTaskDelayUntil			1
+#define INCLUDE_vTaskDelay				1
+
+/* Use the system definition, if there is one */
 #ifdef __NVIC_PRIO_BITS
 #ifdef __NVIC_PRIO_BITS
-  /* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
-  #define configPRIO_BITS                   __NVIC_PRIO_BITS
+	#define configPRIO_BITS       		__NVIC_PRIO_BITS
 #else
 #else
-  #define configPRIO_BITS                   4        /* 15 priority levels */
+	#define configPRIO_BITS       		4        /* 15 priority levels */
 #endif
 #endif
 
 
-/* The lowest interrupt priority that can be used in a call to a "set priority"
-function. */
-#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY         0x0f
-
-/* The highest interrupt priority that can be used by any interrupt service
-routine that makes calls to interrupt safe FreeRTOS API functions.  DO NOT CALL
-INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
-PRIORITY THAN THIS! (higher priorities are lower numeric values. */
-#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY    0x05
+#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY			0xf
+#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY	5
 
 
-/* Interrupt priorities used by the kernel port layer itself.  These are generic
-to all Cortex-M ports, and do not rely on any particular library functions. */
-#define configKERNEL_INTERRUPT_PRIORITY     ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
+/* The lowest priority. */
+#define configKERNEL_INTERRUPT_PRIORITY 	( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
+/* Priority 5, or 160 as only the top three bits are implemented. */
 /* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!!
 /* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!!
 See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
 See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
-#define configMAX_SYSCALL_INTERRUPT_PRIORITY   ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
-
-/* Normal assert() semantics without relying on the provision of an assert.h
-header file. */
-#define configASSERT( x ) if( ( x ) == 0 ) { taskDISABLE_INTERRUPTS(); for( ;; ); }
-
-/* Definitions that map the FreeRTOS port interrupt handlers to their CMSIS
-standard names. */
+#define configMAX_SYSCALL_INTERRUPT_PRIORITY 	( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
+	
+#define configASSERT( x ) if( ( x ) == 0 ) { taskDISABLE_INTERRUPTS(); for( ;; ); }	
+	
 #define vPortSVCHandler SVC_Handler
 #define vPortSVCHandler SVC_Handler
 #define xPortPendSVHandler PendSV_Handler
 #define xPortPendSVHandler PendSV_Handler
+#define vPortSVCHandler SVC_Handler
 #define xPortSysTickHandler SysTick_Handler
 #define xPortSysTickHandler SysTick_Handler
 
 
-#ifdef __cplusplus
-    }
-#endif
+/* Dimensions a buffer that can be used by the FreeRTOS+CLI command
+interpreter.  Set this value to 1 to save RAM if FreeRTOS+CLI does not supply
+the output butter.  See the FreeRTOS+CLI documentation for more information:
+http://www.FreeRTOS.org/FreeRTOS-Plus/FreeRTOS_Plus_CLI/ */
+#define configCOMMAND_INT_MAX_OUTPUT_SIZE			3*1024
 
 
 #endif /* FREERTOS_CONFIG_H */
 #endif /* FREERTOS_CONFIG_H */
 
 
+  

+ 348 - 348
fw/user/lwipopts.h

@@ -1,348 +1,348 @@
-/**
-  ******************************************************************************
-  * @file    lwipopts.h
-  * @author  MCD Application Team
-  * @version V1.0.0
-  * @date    31-October-2011
-  * @brief   lwIP Options Configuration.
-  *          This file is based on Utilities\lwip_v1.3.2\src\include\lwip\opt.h 
-  *          and contains the lwIP configuration for the STM32F4x7 demonstration.
-  ******************************************************************************
-  * @attention
-  *
-  * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
-  * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
-  * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
-  * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
-  * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
-  * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
-  *
-  * <h2><center>&copy; COPYRIGHT 2011 STMicroelectronics</center></h2>
-  ******************************************************************************
-  */
-
-#ifndef __LWIPOPTS_H__
-#define __LWIPOPTS_H__
-
-/**
- * SYS_LIGHTWEIGHT_PROT==1: if you want inter-task protection for certain
- * critical regions during buffer allocation, deallocation and memory
- * allocation and deallocation.
- */
-#define SYS_LIGHTWEIGHT_PROT    1
-
-#define ETHARP_TRUST_IP_MAC     0
-#define IP_REASSEMBLY           0
-#define IP_FRAG                 0
-#define ARP_QUEUEING            0
-
-/**
- * NO_SYS==1: Provides VERY minimal functionality. Otherwise,
- * use lwIP facilities.
- */
-
-#define NO_SYS                  0
-
-/* ---------- Memory options ---------- */
-/* MEM_ALIGNMENT: should be set to the alignment of the CPU for which
-   lwIP is compiled. 4 byte alignment -> define MEM_ALIGNMENT to 4, 2
-   byte alignment -> define MEM_ALIGNMENT to 2. */
-#define MEM_ALIGNMENT           4
-
-/* MEM_SIZE: the size of the heap memory. If the application will send
-a lot of data that needs tьщтo be copied, this should be set high. */
-#define MEM_SIZE                (10*1024)
-
-/* MEMP_NUM_PBUF: the number of memp struct pbufs. If the application
-   sends a lot of data out of ROM (or other static memory), this
-   should be set high. */
-#define MEMP_NUM_PBUF           50  // было
-//#define MEMP_NUM_PBUF           20 // тест
-/* MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. One
-   per active UDP "connection". */
-#define MEMP_NUM_UDP_PCB        6
-/* MEMP_NUM_TCP_PCB: the number of simulatenously active TCP
-   connections. */
-#define MEMP_NUM_TCP_PCB        12 // было
-//#define MEMP_NUM_TCP_PCB        80
-
-/* MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCP
-   connections. */
-#define MEMP_NUM_TCP_PCB_LISTEN 5
-/* MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP
-   segments. */
-#define MEMP_NUM_TCP_SEG        25 // было
-//#define MEMP_NUM_TCP_SEG        50 
-/* MEMP_NUM_SYS_TIMEOUT: the number of simulateously active
-   timeouts. */
-#define MEMP_NUM_SYS_TIMEOUT    10
-
-/**
- * MEMP_NUM_NETCONN: the number of struct netconns.
- * (only needed if you use the sequential API, like api_lib.c)
- */
-#define MEMP_NUM_NETCONN                10
-
-/* ---------- Pbuf options ---------- */
-/* PBUF_POOL_SIZE: the number of buffers in the pbuf pool. */
-#define PBUF_POOL_SIZE          25
-
-/* PBUF_POOL_BUFSIZE: the size of each pbuf in the pbuf pool. */
-#define PBUF_POOL_BUFSIZE         1540 // было
-//#define PBUF_POOL_BUFSIZE         2048 // тесты
-
-
-/**
- * MEMP_NUM_NETBUF: the number of struct netbufs.
- * (only needed if you use the sequential API, like api_lib.c)
- */
-
-#define MEMP_NUM_NETBUF                 7
-
-
-#define TCP_LISTEN_BACKLOG              1
-
-
-/* ---------- TCP options ---------- */
-#define LWIP_TCP                1
-#define TCP_TTL                 255
-
-/* Controls if TCP should queue segments that arrive out of
-   order. Define to 0 if your device is low on memory. */
-#define TCP_QUEUE_OOSEQ         0
-
-/* TCP Maximum segment size. */
-#define TCP_MSS                 1400//(1500 - 40)	  /* TCP_MSS = (Ethernet MTU - IP header size - TCP header size) */
-
-/* TCP sender buffer space (bytes). */
-//#define TCP_SND_BUF             (5*TCP_MSS)
-//#define TCP_SND_BUF             (18*TCP_MSS)
-#define TCP_SND_BUF             (18*TCP_MSS)
-
-/*  TCP_SND_QUEUELEN: TCP sender buffer space (pbufs). This must be at least
-  as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work. */
-//20 
-#define TCP_SND_QUEUELEN        (4* TCP_SND_BUF/TCP_MSS)
-//#define TCP_SND_QUEUELEN        (4* TCP_SND_BUF/TCP_MSS)
-
-/* TCP receive window. */
-#define TCP_WND                 (4*TCP_MSS)
-
-/**
- * TCP_MAXRTX: Maximum number of retransmissions of data segments.
- */
-#define TCP_MAXRTX              6
-
-/* ---------- ICMP options ---------- */
-#define LWIP_ICMP                       1
-//#define ICMP_TTL			128
-
-/* ---------- DHCP options ---------- */
-/* Define LWIP_DHCP to 1 if you want DHCP configuration of
-   interfaces. DHCP is not implemented in lwIP 0.5.1, however, so
-   turning this on does currently not work. */
-#define LWIP_DHCP               1
-
-// ---------- SNMP options ----------
-#define LWIP_SNMP                       0
-#define SNMP_LWIP_MIB2                  0
-#define MIB2_STATS                      0
-#define SNMP_USE_RAW                    0
-#define SNMP_USE_NETCONN                1
-//#define SNMP_CONCURRENT_REQUESTS        1
-//#define SNMP_TRAP_DESTINATIONS          1
-//#define SNMP_PRIVATE_MIB                1
-//#define SNMP_SAFE_REQUESTS              1
-     
-
-/* ---------- UDP options ---------- */
-#define LWIP_UDP                1
-#define UDP_TTL                 255
-
-#define LWIP_SO_RCVTIMEO        1 // Эта опция необходима для netconn_set_recvtimeout в модуле udp_netsetting.c
-#define LWIP_SO_SNDTIMEO        1 //
-
-/* ---------- Statistics options ---------- */
-#define LWIP_STATS                      1
-#define LWIP_STATS_DISPLAY              1
-#define LWIP_PROVIDE_ERRNO              1
-
-#define LWIP_NETIF_LINK_CALLBACK        0
-
-/*
-   --------------------------------------
-   ---------- Checksum options ----------
-   --------------------------------------
-*/
-
-/* 
-The STM32F4x7 allows computing and verifying the IP, UDP, TCP and ICMP checksums by hardware:
- - To use this feature let the following define uncommented.
- - To disable it and process by CPU comment the  the checksum.
-*/
-//#define CHECKSUM_BY_HARDWARE 
-
-
-#ifdef CHECKSUM_BY_HARDWARE
-  /* CHECKSUM_GEN_IP==0: Generate checksums by hardware for outgoing IP packets.*/
-  #define CHECKSUM_GEN_IP                 0
-  /* CHECKSUM_GEN_UDP==0: Generate checksums by hardware for outgoing UDP packets.*/
-  #define CHECKSUM_GEN_UDP                0
-  /* CHECKSUM_GEN_TCP==0: Generate checksums by hardware for outgoing TCP packets.*/
-  #define CHECKSUM_GEN_TCP                0 
-  /* CHECKSUM_CHECK_IP==0: Check checksums by hardware for incoming IP packets.*/
-  #define CHECKSUM_CHECK_IP               0
-  /* CHECKSUM_CHECK_UDP==0: Check checksums by hardware for incoming UDP packets.*/
-  #define CHECKSUM_CHECK_UDP              0
-  /* CHECKSUM_CHECK_TCP==0: Check checksums by hardware for incoming TCP packets.*/
-  #define CHECKSUM_CHECK_TCP              0
-  /* CHECKSUM_CHECK_ICMP==0: Check checksums by hardware for incoming ICMP packets.*/  
-  #define CHECKSUM_GEN_ICMP               0
-#else
-  /* CHECKSUM_GEN_IP==1: Generate checksums in software for outgoing IP packets.*/
-  #define CHECKSUM_GEN_IP                 1
-  /* CHECKSUM_GEN_UDP==1: Generate checksums in software for outgoing UDP packets.*/
-  #define CHECKSUM_GEN_UDP                1
-  /* CHECKSUM_GEN_TCP==1: Generate checksums in software for outgoing TCP packets.*/
-  #define CHECKSUM_GEN_TCP                1
-  /* CHECKSUM_CHECK_IP==1: Check checksums in software for incoming IP packets.*/
-  #define CHECKSUM_CHECK_IP               1
-  /* CHECKSUM_CHECK_UDP==1: Check checksums in software for incoming UDP packets.*/
-  #define CHECKSUM_CHECK_UDP              1
-  /* CHECKSUM_CHECK_TCP==1: Check checksums in software for incoming TCP packets.*/
-  #define CHECKSUM_CHECK_TCP              1
-  /* CHECKSUM_CHECK_ICMP==1: Check checksums by hardware for incoming ICMP packets.*/  
-  #define CHECKSUM_GEN_ICMP               1
-#endif
-
-
-/*
-   ----------------------------------------------
-   ---------- Sequential layer options ----------
-   ----------------------------------------------
-*/
-
-#define LWIP_RAW                        1   
-
-/**
- * LWIP_NETCONN==1: Enable Netconn API (require to use api_lib.c)
- */
-#define LWIP_NETCONN                    1
-
-/*
-   ------------------------------------
-   ---------- Socket options ----------
-   ------------------------------------
-*/
-/**
- * LWIP_SOCKET==1: Enable Socket API (require to use sockets.c)
- */
-#define LWIP_SOCKET                     1
-#define LWIP_DNS                        1
-#define SO_REUSE                        1
-   
-/*
-   ------------------------------------
-   ----------- SNTP options -----------
-   ------------------------------------
-*/   
-#define SNTP_MAX_SERVERS                2
-#define SNTP_SERVER_DNS                 1
-#define SNTP_UPDATE_DELAY               86400000 // 24 часа
-#define SNTP_SET_SYSTEM_TIME(t)         lwip_time_sinhro(t)
-#define SNTP_STARTUP_DELAY              1
-#define SNTP_STARTUP_DELAY_FUNC         10000
-
-
-/*
-   ------------------------------------
-   ------------ PPP options -----------
-   ------------------------------------
-*/
-#define PPP_SUPPORT                     1
-#define PPPOS_SUPPORT                   1  
-#define PPP_INPROC_IRQ_SAFE             0
-#define PRINTPKT_SUPPORT                0
-#define PPP_IPV4_SUPPORT                1
-   
-#define PAP_SUPPORT                     1   
-#define CHAP_SUPPORT                    0
-#define MSCHAP_SUPPORT                  0   
-#define EAP_SUPPORT                     0
-#define CCP_SUPPORT                     0
-#define MPPE_SUPPORT                    0
-
-
-
-/*
-   ------------------------------------
-   ------------ MQTT options ----------
-   ------------------------------------
-*/
-#define MQTT_OUTPUT_RINGBUF_SIZE        512
-
-#define MQTT_VAR_HEADER_BUFFER_LEN      512
-
-
-/*
-   -----------------------------------
-   ----------- HOOK options ----------
-   -----------------------------------
-*/
-//#define LWIP_HOOK_FILENAME "lwip_route_hook.h"
-//#include LWIP_HOOK_FILENAME
-
-#define LWIP_TCPIP_THREAD_ALIVE() lwip_alive()
-
-/*
-   -----------------------------------
-   ---------- DEBUG options ----------
-   -----------------------------------
-*/
-#define LWIP_DEBUG
-
-#define TCP_DEBUG                       LWIP_DBG_OFF
-#define TCP_RTO_DEBUG                   LWIP_DBG_OFF
-#define TCP_CWND_DEBUG                  LWIP_DBG_OFF
-#define ETHARP_DEBUG                    LWIP_DBG_OFF
-#define PBUF_DEBUG                      LWIP_DBG_OFF
-#define IP_DEBUG                        LWIP_DBG_OFF
-#define TCPIP_DEBUG                     LWIP_DBG_OFF
-#define DHCP_DEBUG                      LWIP_DBG_OFF
-#define UDP_DEBUG                       LWIP_DBG_OFF
-#define SOCKETS_DEBUG                   LWIP_DBG_OFF
-#define MEMP_DEBUG                      LWIP_DBG_OFF
-#define PING_DEBUG                      LWIP_DBG_OFF
-#define SNMP_DEBUG                      LWIP_DBG_OFF
-#define SNMP_MIB_DEBUG                  LWIP_DBG_OFF
-#define TIMERS_DEBUG                    LWIP_DBG_OFF
-#define ICMP_DEBUG                      LWIP_DBG_OFF
-#define NETIF_DEBUG                     LWIP_DBG_OFF
-#define PPP_DEBUG                       LWIP_DBG_OFF
-#define SNTP_DEBUG                      LWIP_DBG_OFF
-#define MQTT_DEBUG                      LWIP_DBG_OFF
-
-/*
-   ---------------------------------
-   ---------- OS options ----------
-   ---------------------------------
-*/
-#define TCPIP_THREAD_STACKSIZE          800
-#define TCPIP_MBOX_SIZE                 150//50
-#define DEFAULT_UDP_RECVMBOX_SIZE       20
-#define DEFAULT_TCP_RECVMBOX_SIZE       70
-#define DEFAULT_RAW_RECVMBOX_SIZE       10
-#define DEFAULT_ACCEPTMBOX_SIZE         20
-#define DEFAULT_THREAD_STACKSIZE        500
-#define TCPIP_THREAD_PRIO               (configMAX_PRIORITIES - 2)
-
-#define MEMP_NUM_TCPIP_MSG_INPKT        40//20
-
-
-#define LWIP_DISABLE_TCP_SANITY_CHECKS 1
-
-
-#endif /* __LWIPOPTS_H__ */
-
-/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/
-
+/**
+  ******************************************************************************
+  * @file    lwipopts.h
+  * @author  MCD Application Team
+  * @version V1.0.0
+  * @date    31-October-2011
+  * @brief   lwIP Options Configuration.
+  *          This file is based on Utilities\lwip_v1.3.2\src\include\lwip\opt.h 
+  *          and contains the lwIP configuration for the STM32F4x7 demonstration.
+  ******************************************************************************
+  * @attention
+  *
+  * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
+  * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
+  * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
+  * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
+  * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
+  * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
+  *
+  * <h2><center>&copy; COPYRIGHT 2011 STMicroelectronics</center></h2>
+  ******************************************************************************
+  */
+
+#ifndef __LWIPOPTS_H__
+#define __LWIPOPTS_H__
+
+/**
+ * SYS_LIGHTWEIGHT_PROT==1: if you want inter-task protection for certain
+ * critical regions during buffer allocation, deallocation and memory
+ * allocation and deallocation.
+ */
+#define SYS_LIGHTWEIGHT_PROT    1
+
+#define ETHARP_TRUST_IP_MAC     0
+#define IP_REASSEMBLY           0
+#define IP_FRAG                 0
+#define ARP_QUEUEING            0
+
+/**
+ * NO_SYS==1: Provides VERY minimal functionality. Otherwise,
+ * use lwIP facilities.
+ */
+
+#define NO_SYS                  0
+
+/* ---------- Memory options ---------- */
+/* MEM_ALIGNMENT: should be set to the alignment of the CPU for which
+   lwIP is compiled. 4 byte alignment -> define MEM_ALIGNMENT to 4, 2
+   byte alignment -> define MEM_ALIGNMENT to 2. */
+#define MEM_ALIGNMENT           4
+
+/* MEM_SIZE: the size of the heap memory. If the application will send
+a lot of data that needs tьщтo be copied, this should be set high. */
+#define MEM_SIZE                (10*1024)
+
+/* MEMP_NUM_PBUF: the number of memp struct pbufs. If the application
+   sends a lot of data out of ROM (or other static memory), this
+   should be set high. */
+#define MEMP_NUM_PBUF           50  // было
+//#define MEMP_NUM_PBUF           20 // тест
+/* MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. One
+   per active UDP "connection". */
+#define MEMP_NUM_UDP_PCB        6
+/* MEMP_NUM_TCP_PCB: the number of simulatenously active TCP
+   connections. */
+#define MEMP_NUM_TCP_PCB        12 // было
+//#define MEMP_NUM_TCP_PCB        80
+
+/* MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCP
+   connections. */
+#define MEMP_NUM_TCP_PCB_LISTEN 5
+/* MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP
+   segments. */
+#define MEMP_NUM_TCP_SEG        25 // было
+//#define MEMP_NUM_TCP_SEG        50 
+/* MEMP_NUM_SYS_TIMEOUT: the number of simulateously active
+   timeouts. */
+#define MEMP_NUM_SYS_TIMEOUT    10
+
+/**
+ * MEMP_NUM_NETCONN: the number of struct netconns.
+ * (only needed if you use the sequential API, like api_lib.c)
+ */
+#define MEMP_NUM_NETCONN                10
+
+/* ---------- Pbuf options ---------- */
+/* PBUF_POOL_SIZE: the number of buffers in the pbuf pool. */
+#define PBUF_POOL_SIZE          25
+
+/* PBUF_POOL_BUFSIZE: the size of each pbuf in the pbuf pool. */
+#define PBUF_POOL_BUFSIZE         1540 // было
+//#define PBUF_POOL_BUFSIZE         2048 // тесты
+
+
+/**
+ * MEMP_NUM_NETBUF: the number of struct netbufs.
+ * (only needed if you use the sequential API, like api_lib.c)
+ */
+
+#define MEMP_NUM_NETBUF                 7
+
+
+#define TCP_LISTEN_BACKLOG              1
+
+
+/* ---------- TCP options ---------- */
+#define LWIP_TCP                1
+#define TCP_TTL                 255
+
+/* Controls if TCP should queue segments that arrive out of
+   order. Define to 0 if your device is low on memory. */
+#define TCP_QUEUE_OOSEQ         0
+
+/* TCP Maximum segment size. */
+#define TCP_MSS                 1400//(1500 - 40)	  /* TCP_MSS = (Ethernet MTU - IP header size - TCP header size) */
+
+/* TCP sender buffer space (bytes). */
+//#define TCP_SND_BUF             (5*TCP_MSS)
+//#define TCP_SND_BUF             (18*TCP_MSS)
+#define TCP_SND_BUF             (18*TCP_MSS)
+
+/*  TCP_SND_QUEUELEN: TCP sender buffer space (pbufs). This must be at least
+  as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work. */
+//20 
+#define TCP_SND_QUEUELEN        (4* TCP_SND_BUF/TCP_MSS)
+//#define TCP_SND_QUEUELEN        (4* TCP_SND_BUF/TCP_MSS)
+
+/* TCP receive window. */
+#define TCP_WND                 (4*TCP_MSS)
+
+/**
+ * TCP_MAXRTX: Maximum number of retransmissions of data segments.
+ */
+#define TCP_MAXRTX              6
+
+/* ---------- ICMP options ---------- */
+#define LWIP_ICMP                       1
+//#define ICMP_TTL			128
+
+/* ---------- DHCP options ---------- */
+/* Define LWIP_DHCP to 1 if you want DHCP configuration of
+   interfaces. DHCP is not implemented in lwIP 0.5.1, however, so
+   turning this on does currently not work. */
+#define LWIP_DHCP               1
+
+// ---------- SNMP options ----------
+#define LWIP_SNMP                       0
+#define SNMP_LWIP_MIB2                  0
+#define MIB2_STATS                      0
+#define SNMP_USE_RAW                    0
+#define SNMP_USE_NETCONN                1
+//#define SNMP_CONCURRENT_REQUESTS        1
+//#define SNMP_TRAP_DESTINATIONS          1
+//#define SNMP_PRIVATE_MIB                1
+//#define SNMP_SAFE_REQUESTS              1
+     
+
+/* ---------- UDP options ---------- */
+#define LWIP_UDP                1
+#define UDP_TTL                 255
+
+#define LWIP_SO_RCVTIMEO        1 // Эта опция необходима для netconn_set_recvtimeout в модуле udp_netsetting.c
+#define LWIP_SO_SNDTIMEO        1 //
+
+/* ---------- Statistics options ---------- */
+#define LWIP_STATS                      1
+#define LWIP_STATS_DISPLAY              1
+#define LWIP_PROVIDE_ERRNO              1
+
+#define LWIP_NETIF_LINK_CALLBACK        0
+
+/*
+   --------------------------------------
+   ---------- Checksum options ----------
+   --------------------------------------
+*/
+
+/* 
+The STM32F4x7 allows computing and verifying the IP, UDP, TCP and ICMP checksums by hardware:
+ - To use this feature let the following define uncommented.
+ - To disable it and process by CPU comment the  the checksum.
+*/
+//#define CHECKSUM_BY_HARDWARE 
+
+
+#ifdef CHECKSUM_BY_HARDWARE
+  /* CHECKSUM_GEN_IP==0: Generate checksums by hardware for outgoing IP packets.*/
+  #define CHECKSUM_GEN_IP                 0
+  /* CHECKSUM_GEN_UDP==0: Generate checksums by hardware for outgoing UDP packets.*/
+  #define CHECKSUM_GEN_UDP                0
+  /* CHECKSUM_GEN_TCP==0: Generate checksums by hardware for outgoing TCP packets.*/
+  #define CHECKSUM_GEN_TCP                0 
+  /* CHECKSUM_CHECK_IP==0: Check checksums by hardware for incoming IP packets.*/
+  #define CHECKSUM_CHECK_IP               0
+  /* CHECKSUM_CHECK_UDP==0: Check checksums by hardware for incoming UDP packets.*/
+  #define CHECKSUM_CHECK_UDP              0
+  /* CHECKSUM_CHECK_TCP==0: Check checksums by hardware for incoming TCP packets.*/
+  #define CHECKSUM_CHECK_TCP              0
+  /* CHECKSUM_CHECK_ICMP==0: Check checksums by hardware for incoming ICMP packets.*/  
+  #define CHECKSUM_GEN_ICMP               0
+#else
+  /* CHECKSUM_GEN_IP==1: Generate checksums in software for outgoing IP packets.*/
+  #define CHECKSUM_GEN_IP                 1
+  /* CHECKSUM_GEN_UDP==1: Generate checksums in software for outgoing UDP packets.*/
+  #define CHECKSUM_GEN_UDP                1
+  /* CHECKSUM_GEN_TCP==1: Generate checksums in software for outgoing TCP packets.*/
+  #define CHECKSUM_GEN_TCP                1
+  /* CHECKSUM_CHECK_IP==1: Check checksums in software for incoming IP packets.*/
+  #define CHECKSUM_CHECK_IP               1
+  /* CHECKSUM_CHECK_UDP==1: Check checksums in software for incoming UDP packets.*/
+  #define CHECKSUM_CHECK_UDP              1
+  /* CHECKSUM_CHECK_TCP==1: Check checksums in software for incoming TCP packets.*/
+  #define CHECKSUM_CHECK_TCP              1
+  /* CHECKSUM_CHECK_ICMP==1: Check checksums by hardware for incoming ICMP packets.*/  
+  #define CHECKSUM_GEN_ICMP               1
+#endif
+
+
+/*
+   ----------------------------------------------
+   ---------- Sequential layer options ----------
+   ----------------------------------------------
+*/
+
+#define LWIP_RAW                        1   
+
+/**
+ * LWIP_NETCONN==1: Enable Netconn API (require to use api_lib.c)
+ */
+#define LWIP_NETCONN                    1
+
+/*
+   ------------------------------------
+   ---------- Socket options ----------
+   ------------------------------------
+*/
+/**
+ * LWIP_SOCKET==1: Enable Socket API (require to use sockets.c)
+ */
+#define LWIP_SOCKET                     1
+#define LWIP_DNS                        1
+#define SO_REUSE                        1
+   
+/*
+   ------------------------------------
+   ----------- SNTP options -----------
+   ------------------------------------
+*/   
+#define SNTP_MAX_SERVERS                2
+#define SNTP_SERVER_DNS                 1
+#define SNTP_UPDATE_DELAY               86400000 // 24 часа
+#define SNTP_SET_SYSTEM_TIME(t)         lwip_time_sinhro(t)
+#define SNTP_STARTUP_DELAY              1
+#define SNTP_STARTUP_DELAY_FUNC         10000
+
+
+/*
+   ------------------------------------
+   ------------ PPP options -----------
+   ------------------------------------
+*/
+#define PPP_SUPPORT                     0
+#define PPPOS_SUPPORT                   0 
+#define PPP_INPROC_IRQ_SAFE             0
+#define PRINTPKT_SUPPORT                0
+#define PPP_IPV4_SUPPORT                0
+   
+#define PAP_SUPPORT                     1   
+#define CHAP_SUPPORT                    0
+#define MSCHAP_SUPPORT                  0   
+#define EAP_SUPPORT                     0
+#define CCP_SUPPORT                     0
+#define MPPE_SUPPORT                    0
+
+
+
+/*
+   ------------------------------------
+   ------------ MQTT options ----------
+   ------------------------------------
+*/
+#define MQTT_OUTPUT_RINGBUF_SIZE        512
+
+#define MQTT_VAR_HEADER_BUFFER_LEN      512
+
+
+/*
+   -----------------------------------
+   ----------- HOOK options ----------
+   -----------------------------------
+*/
+//#define LWIP_HOOK_FILENAME "lwip_route_hook.h"
+//#include LWIP_HOOK_FILENAME
+
+//#define LWIP_TCPIP_THREAD_ALIVE() lwip_alive()
+
+/*
+   -----------------------------------
+   ---------- DEBUG options ----------
+   -----------------------------------
+*/
+#define LWIP_DEBUG
+
+#define TCP_DEBUG                       LWIP_DBG_OFF
+#define TCP_RTO_DEBUG                   LWIP_DBG_OFF
+#define TCP_CWND_DEBUG                  LWIP_DBG_OFF
+#define ETHARP_DEBUG                    LWIP_DBG_OFF
+#define PBUF_DEBUG                      LWIP_DBG_OFF
+#define IP_DEBUG                        LWIP_DBG_OFF
+#define TCPIP_DEBUG                     LWIP_DBG_OFF
+#define DHCP_DEBUG                      LWIP_DBG_OFF
+#define UDP_DEBUG                       LWIP_DBG_OFF
+#define SOCKETS_DEBUG                   LWIP_DBG_OFF
+#define MEMP_DEBUG                      LWIP_DBG_OFF
+#define PING_DEBUG                      LWIP_DBG_OFF
+#define SNMP_DEBUG                      LWIP_DBG_OFF
+#define SNMP_MIB_DEBUG                  LWIP_DBG_OFF
+#define TIMERS_DEBUG                    LWIP_DBG_OFF
+#define ICMP_DEBUG                      LWIP_DBG_OFF
+#define NETIF_DEBUG                     LWIP_DBG_OFF
+#define PPP_DEBUG                       LWIP_DBG_OFF
+#define SNTP_DEBUG                      LWIP_DBG_OFF
+#define MQTT_DEBUG                      LWIP_DBG_OFF
+
+/*
+   ---------------------------------
+   ---------- OS options ----------
+   ---------------------------------
+*/
+#define TCPIP_THREAD_STACKSIZE          800
+#define TCPIP_MBOX_SIZE                 150//50
+#define DEFAULT_UDP_RECVMBOX_SIZE       20
+#define DEFAULT_TCP_RECVMBOX_SIZE       70
+#define DEFAULT_RAW_RECVMBOX_SIZE       10
+#define DEFAULT_ACCEPTMBOX_SIZE         20
+#define DEFAULT_THREAD_STACKSIZE        500
+#define TCPIP_THREAD_PRIO               (configMAX_PRIORITIES - 2)
+
+#define MEMP_NUM_TCPIP_MSG_INPKT        40//20
+
+
+#define LWIP_DISABLE_TCP_SANITY_CHECKS 1
+
+
+#endif /* __LWIPOPTS_H__ */
+
+/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/
+

+ 25 - 6
fw/user/main.c

@@ -2,7 +2,6 @@
 #include "at32f403a_407_board.h"
 #include "at32f403a_407_board.h"
 #include "at32f403a_407_clock.h"
 #include "at32f403a_407_clock.h"
 #include "common_config.h"
 #include "common_config.h"
-//#include "at32_emac.h"
 #include "FreeRTOS.h"
 #include "FreeRTOS.h"
 #include "task.h"
 #include "task.h"
 #include "queue.h"
 #include "queue.h"
@@ -10,6 +9,12 @@
 #include "usb_eth.h"
 #include "usb_eth.h"
 #include "mux.h"
 #include "mux.h"
 #include "misc.h"
 #include "misc.h"
+#include "spi_common.h"
+#include "user_fatfs.h"
+#include "spi_flash.h"
+#include "usb_eth.h"
+#include "extended_sram.h"
+#include "modbus.h"
 #include <stdio.h>
 #include <stdio.h>
 #include <stdbool.h>
 #include <stdbool.h>
 #include <string.h>
 #include <string.h>
@@ -24,13 +29,15 @@ void usb_clock48m_select(usb_clk48_s clk_s);
 //
 //
 int main(void)
 int main(void)
 {
 {
+    //extend_SRAM();
+      
     nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);
     nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);
   
   
     system_clock_config();
     system_clock_config();
 
 
     at32_board_init();
     at32_board_init();
     
     
-    usb_clock48m_select(USB_CLK_HEXT);
+    //usb_clock48m_select(USB_CLK_HEXT);
     
     
     crm_periph_clock_enable(CRM_USB_PERIPH_CLOCK, TRUE);
     crm_periph_clock_enable(CRM_USB_PERIPH_CLOCK, TRUE);
     
     
@@ -44,7 +51,7 @@ int main(void)
 #if 1
 #if 1
     taskENTER_CRITICAL();      
     taskENTER_CRITICAL();      
     
     
-    xTaskCreate(init_task, "init_task", 2*configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL);
+    xTaskCreate(init_task, "init_task", 10*configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL);
     
     
     xTaskCreate(test_hw_task, "hw_task", 2*configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL);
     xTaskCreate(test_hw_task, "hw_task", 2*configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL);
     
     
@@ -68,12 +75,24 @@ void init_task(void *argument)
     
     
     // Тесты таймеров
     // Тесты таймеров
     mux_led_test_init();
     mux_led_test_init();
-    modbus_tim_init();
+    //modbus_tim_init();
+    
+    // Тесты modbus
+    //modbus_init();
+    mb_init();
+    
+    // Тесты SPI flash
+    common_spi_init();
+    InitFS(PRIM_DRIVE);
+    //spi_flash_test();
+      
+    // Тесты USB
+    //usb_eth_init();
     
     
     for (;;)
     for (;;)
     {
     {
         //printf("%u\r\n", cnt++);
         //printf("%u\r\n", cnt++);
-        //mux_led_proc();
+        mux_led_proc();
         
         
     }
     }
 
 
@@ -95,7 +114,7 @@ void test_hw_task(void *argument)
 //
 //
 void usb_clock48m_select(usb_clk48_s clk_s)
 void usb_clock48m_select(usb_clk48_s clk_s)
 {
 {
-if(clk_s == USB_CLK_HICK)
+  if(clk_s == USB_CLK_HICK)
   {
   {
     crm_usb_clock_source_select(CRM_USB_CLOCK_SOURCE_HICK);
     crm_usb_clock_source_select(CRM_USB_CLOCK_SOURCE_HICK);
 
 

+ 121 - 116
fw/user/usb_conf.h

@@ -1,116 +1,121 @@
-/**
-  **************************************************************************
-  * @file     usb_conf.h
-  * @version  v2.0.6
-  * @date     2021-12-31
-  * @brief    usb config header file
-  **************************************************************************
-  *                       Copyright notice & Disclaimer
-  *
-  * The software Board Support Package (BSP) that is made available to 
-  * download from Artery official website is the copyrighted work of Artery. 
-  * Artery authorizes customers to use, copy, and distribute the BSP 
-  * software and its related documentation for the purpose of design and 
-  * development in conjunction with Artery microcontrollers. Use of the 
-  * software is governed by this copyright notice and the following disclaimer.
-  *
-  * THIS SOFTWARE IS PROVIDED ON "AS IS" BASIS WITHOUT WARRANTIES,
-  * GUARANTEES OR REPRESENTATIONS OF ANY KIND. ARTERY EXPRESSLY DISCLAIMS,
-  * TO THE FULLEST EXTENT PERMITTED BY LAW, ALL EXPRESS, IMPLIED OR
-  * STATUTORY OR OTHER WARRANTIES, GUARANTEES OR REPRESENTATIONS,
-  * INCLUDING BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
-  * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
-  *
-  **************************************************************************
-  */
-
-/* define to prevent recursive inclusion -------------------------------------*/
-#ifndef __USB_CONF_H
-#define __USB_CONF_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include "at32f403a_407.h"
-#include "at32f403a_407_board.h"
-#include "stdio.h"
-
-/** @addtogroup AT32F407_periph_examples
-  * @{
-  */
-  
-/** @addtogroup 407_USB_device_vcp_loopback
-  * @{
-  */
-
-/**
-  * @brief usb endpoint max num define
-  */
-#ifndef USB_EPT_MAX_NUM
-#define USB_EPT_MAX_NUM                   8  /*!< usb device support endpoint number */
-#endif
-
-/**
-  * @brief usb buffer extend to 768-1280 bytes
-  */
-//#define USB_BUFFER_SIZE_EX  /*!< usb enable extend buffer */
-
-
-/**
-  * @brief auto malloc usb endpoint buffer
-  */
-//#define USB_EPT_AUTO_MALLOC_BUFFER  /*!< usb auto malloc endpoint tx and rx buffer */
-
-
-#ifndef USB_EPT_AUTO_MALLOC_BUFFER
-/**
-  * @brief user custom endpoint buffer
-  *        EPTn_TX_ADDR, EPTn_RX_ADDR must less than usb buffer size
-  */
-  
-/* ept0 tx start address 0x40, size 0x40, 
-   so rx start address is 0x40 + 0x40 = 0x80 */
-#define EPT0_TX_ADDR                     0x40    /*!< usb endpoint 0 tx buffer address offset */
-#define EPT0_RX_ADDR                     0x80    /*!< usb endpoint 0 rx buffer address offset */
-
-#define EPT1_TX_ADDR                     0xC0    /*!< usb endpoint 1 tx buffer address offset */
-#define EPT1_RX_ADDR                     0x100   /*!< usb endpoint 1 rx buffer address offset */
-
-#define EPT2_TX_ADDR                     0x140   /*!< usb endpoint 2 tx buffer address offset */
-#define EPT2_RX_ADDR                     0x180   /*!< usb endpoint 2 rx buffer address offset */
-
-#define EPT3_TX_ADDR                     0x00    /*!< usb endpoint 3 tx buffer address offset */
-#define EPT3_RX_ADDR                     0x00    /*!< usb endpoint 3 rx buffer address offset */
-
-#define EPT4_TX_ADDR                     0x00    /*!< usb endpoint 4 tx buffer address offset */
-#define EPT4_RX_ADDR                     0x00    /*!< usb endpoint 4 rx buffer address offset */
-
-#define EPT5_TX_ADDR                     0x00    /*!< usb endpoint 5 tx buffer address offset */
-#define EPT5_RX_ADDR                     0x00    /*!< usb endpoint 5 rx buffer address offset */
-
-#define EPT6_TX_ADDR                     0x00    /*!< usb endpoint 6 tx buffer address offset */
-#define EPT6_RX_ADDR                     0x00    /*!< usb endpoint 6 rx buffer address offset */
-
-#define EPT7_TX_ADDR                     0x00    /*!< usb endpoint 7 tx buffer address offset */
-#define EPT7_RX_ADDR                     0x00    /*!< usb endpoint 7 rx buffer address offset */
-
-#endif
-
-void usb_delay_ms(uint32_t ms);
-void usb_delay_us(uint32_t us);
-
-/**
-  * @}
-  */ 
-
-/**
-  * @}
-  */ 
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
-
+/**
+  **************************************************************************
+  * @file     usb_conf.h
+  * @version  v2.0.6
+  * @date     2021-12-31
+  * @brief    usb config header file
+  **************************************************************************
+  *                       Copyright notice & Disclaimer
+  *
+  * The software Board Support Package (BSP) that is made available to 
+  * download from Artery official website is the copyrighted work of Artery. 
+  * Artery authorizes customers to use, copy, and distribute the BSP 
+  * software and its related documentation for the purpose of design and 
+  * development in conjunction with Artery microcontrollers. Use of the 
+  * software is governed by this copyright notice and the following disclaimer.
+  *
+  * THIS SOFTWARE IS PROVIDED ON "AS IS" BASIS WITHOUT WARRANTIES,
+  * GUARANTEES OR REPRESENTATIONS OF ANY KIND. ARTERY EXPRESSLY DISCLAIMS,
+  * TO THE FULLEST EXTENT PERMITTED BY LAW, ALL EXPRESS, IMPLIED OR
+  * STATUTORY OR OTHER WARRANTIES, GUARANTEES OR REPRESENTATIONS,
+  * INCLUDING BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
+  * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+  *
+  **************************************************************************
+  */
+
+/* define to prevent recursive inclusion -------------------------------------*/
+#ifndef __USB_CONF_H
+#define __USB_CONF_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "at32f403a_407.h"
+#include "at32f403a_407_board.h"
+#ifdef PRINTF_STDLIB
+#include <stdio.h>
+#endif
+#ifdef PRINTF_CUSTOM
+#include "tinystdio.h"
+#endif
+
+/** @addtogroup AT32F407_periph_examples
+  * @{
+  */
+  
+/** @addtogroup 407_USB_device_vcp_loopback
+  * @{
+  */
+
+/**
+  * @brief usb endpoint max num define
+  */
+#ifndef USB_EPT_MAX_NUM
+#define USB_EPT_MAX_NUM                   8  /*!< usb device support endpoint number */
+#endif
+
+/**
+  * @brief usb buffer extend to 768-1280 bytes
+  */
+//#define USB_BUFFER_SIZE_EX  /*!< usb enable extend buffer */
+
+
+/**
+  * @brief auto malloc usb endpoint buffer
+  */
+//#define USB_EPT_AUTO_MALLOC_BUFFER  /*!< usb auto malloc endpoint tx and rx buffer */
+
+
+#ifndef USB_EPT_AUTO_MALLOC_BUFFER
+/**
+  * @brief user custom endpoint buffer
+  *        EPTn_TX_ADDR, EPTn_RX_ADDR must less than usb buffer size
+  */
+  
+/* ept0 tx start address 0x40, size 0x40, 
+   so rx start address is 0x40 + 0x40 = 0x80 */
+#define EPT0_TX_ADDR                     0x40    /*!< usb endpoint 0 tx buffer address offset */
+#define EPT0_RX_ADDR                     0x80    /*!< usb endpoint 0 rx buffer address offset */
+
+#define EPT1_TX_ADDR                     0xC0    /*!< usb endpoint 1 tx buffer address offset */
+#define EPT1_RX_ADDR                     0x100   /*!< usb endpoint 1 rx buffer address offset */
+
+#define EPT2_TX_ADDR                     0x140   /*!< usb endpoint 2 tx buffer address offset */
+#define EPT2_RX_ADDR                     0x180   /*!< usb endpoint 2 rx buffer address offset */
+
+#define EPT3_TX_ADDR                     0x00    /*!< usb endpoint 3 tx buffer address offset */
+#define EPT3_RX_ADDR                     0x00    /*!< usb endpoint 3 rx buffer address offset */
+
+#define EPT4_TX_ADDR                     0x00    /*!< usb endpoint 4 tx buffer address offset */
+#define EPT4_RX_ADDR                     0x00    /*!< usb endpoint 4 rx buffer address offset */
+
+#define EPT5_TX_ADDR                     0x00    /*!< usb endpoint 5 tx buffer address offset */
+#define EPT5_RX_ADDR                     0x00    /*!< usb endpoint 5 rx buffer address offset */
+
+#define EPT6_TX_ADDR                     0x00    /*!< usb endpoint 6 tx buffer address offset */
+#define EPT6_RX_ADDR                     0x00    /*!< usb endpoint 6 rx buffer address offset */
+
+#define EPT7_TX_ADDR                     0x00    /*!< usb endpoint 7 tx buffer address offset */
+#define EPT7_RX_ADDR                     0x00    /*!< usb endpoint 7 rx buffer address offset */
+
+#endif
+
+void usb_delay_ms(uint32_t ms);
+void usb_delay_us(uint32_t us);
+
+/**
+  * @}
+  */ 
+
+/**
+  * @}
+  */ 
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+

+ 1 - 1
libs/artery/cmsis/cm4/device_support/system_at32f403a_407.c

@@ -84,7 +84,7 @@ void SystemInit (void)
 
 
   /* reset cfg register, include sclk switch, ahbdiv, apb1div, apb2div, adcdiv,
   /* reset cfg register, include sclk switch, ahbdiv, apb1div, apb2div, adcdiv,
      clkout pllrcs, pllhextdiv, pllmult, usbdiv and pllrange bits */
      clkout pllrcs, pllhextdiv, pllmult, usbdiv and pllrange bits */
-  CRM->cfg = 0;
+  CRM->cfg = 0; 
 
 
   /* reset clkout[3], usbbufs, hickdiv, clkoutdiv */
   /* reset clkout[3], usbbufs, hickdiv, clkoutdiv */
   CRM->misc1 = 0;
   CRM->misc1 = 0;

+ 3 - 1
libs/artery/drivers/src/at32f403a_407_flash.c

@@ -617,12 +617,14 @@ flash_status_type flash_user_system_data_erase(void)
   /* unlock the user system data */
   /* unlock the user system data */
   FLASH->usd_unlock = FLASH_UNLOCK_KEY1;
   FLASH->usd_unlock = FLASH_UNLOCK_KEY1;
   FLASH->usd_unlock = FLASH_UNLOCK_KEY2;
   FLASH->usd_unlock = FLASH_UNLOCK_KEY2;
+
   while(FLASH->ctrl_bit.usdulks==RESET);
   while(FLASH->ctrl_bit.usdulks==RESET);
 
 
   /* erase the user system data */
   /* erase the user system data */
   FLASH->ctrl_bit.usders = TRUE;
   FLASH->ctrl_bit.usders = TRUE;
   FLASH->ctrl_bit.erstr = TRUE;
   FLASH->ctrl_bit.erstr = TRUE;
 
 
+#if 1
   /* wait for operation to be completed */
   /* wait for operation to be completed */
   status = flash_operation_wait_for(ERASE_TIMEOUT);
   status = flash_operation_wait_for(ERASE_TIMEOUT);
 
 
@@ -643,7 +645,7 @@ flash_status_type flash_user_system_data_erase(void)
     /*disable the usdprgm bit */
     /*disable the usdprgm bit */
     FLASH->ctrl_bit.usdprgm = FALSE;
     FLASH->ctrl_bit.usdprgm = FALSE;
   }
   }
-
+#endif
   /* return the erase status */
   /* return the erase status */
   return status;
   return status;
 }
 }

+ 342 - 342
libs/thirdparty/rndis/usbd_drivers/inc/usb_std.h → libs/artery/usb/inc/usb_std.h

@@ -1,342 +1,342 @@
-/**
-  **************************************************************************
-  * @file     usb_std.h
-  * @version  v2.0.6
-  * @date     2021-12-31
-  * @brief    usb standard header file
-  **************************************************************************
-  *                       Copyright notice & Disclaimer
-  *
-  * The software Board Support Package (BSP) that is made available to 
-  * download from Artery official website is the copyrighted work of Artery. 
-  * Artery authorizes customers to use, copy, and distribute the BSP 
-  * software and its related documentation for the purpose of design and 
-  * development in conjunction with Artery microcontrollers. Use of the 
-  * software is governed by this copyright notice and the following disclaimer.
-  *
-  * THIS SOFTWARE IS PROVIDED ON "AS IS" BASIS WITHOUT WARRANTIES,
-  * GUARANTEES OR REPRESENTATIONS OF ANY KIND. ARTERY EXPRESSLY DISCLAIMS,
-  * TO THE FULLEST EXTENT PERMITTED BY LAW, ALL EXPRESS, IMPLIED OR
-  * STATUTORY OR OTHER WARRANTIES, GUARANTEES OR REPRESENTATIONS,
-  * INCLUDING BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
-  * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
-  *
-  **************************************************************************
-  */
-
-/* define to prevent recursive inclusion -------------------------------------*/
-#ifndef __USB_STD_H
-#define __USB_STD_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-
-/* includes ------------------------------------------------------------------*/
-#include "usb_conf.h"
-
-/** @addtogroup AT32F403A_407_middlewares_usbd_drivers
-  * @{
-  */
-  
-/** @addtogroup USB_standard
-  * @{
-  */
-
-/** @defgroup USB_standard_define
-  * @{
-  */
-
-/**
-  * @brief usb request recipient
-  */
-#define USB_REQ_RECIPIENT_DEVICE         0x00 /*!< usb request recipient device */
-#define USB_REQ_RECIPIENT_INTERFACE      0x01 /*!< usb request recipient interface */
-#define USB_REQ_RECIPIENT_ENDPOINT       0x02 /*!< usb request recipient endpoint */
-#define USB_REQ_RECIPIENT_OTHER          0x03 /*!< usb request recipient other */
-#define USB_REQ_RECIPIENT_MASK           0x1F /*!< usb request recipient mask */
-
-/**
-  * @brief usb request type
-  */
-#define USB_REQ_TYPE_STANDARD            0x00 /*!< usb request type standard */
-#define USB_REQ_TYPE_CLASS               0x20 /*!< usb request type class */
-#define USB_REQ_TYPE_VENDOR              0x40 /*!< usb request type vendor */
-#define USB_REQ_TYPE_RESERVED            0x60 /*!< usb request type reserved */
-
-/**
-  * @brief usb request data transfer direction
-  */
-#define USB_REQ_DIR_HTD                  0x00 /*!< usb request data transfer direction host to device */
-#define USB_REQ_DIR_DTH                  0x80 /*!< usb request data transfer direction device to host */
-
-/**
-  * @brief usb standard device requests codes
-  */
-#define USB_STD_REQ_GET_STATUS           0 /*!< usb request code status */
-#define USB_STD_REQ_CLEAR_FEATURE        1 /*!< usb request code clear feature */
-#define USB_STD_REQ_SET_FEATURE          3 /*!< usb request code feature */
-#define USB_STD_REQ_SET_ADDRESS          5 /*!< usb request code address */
-#define USB_STD_REQ_GET_DESCRIPTOR       6 /*!< usb request code get descriptor */
-#define USB_STD_REQ_SET_DESCRIPTOR       7 /*!< usb request code set descriptor */
-#define USB_STD_REQ_GET_CONFIGURATION    8 /*!< usb request code get configuration */
-#define USB_STD_REQ_SET_CONFIGURATION    9 /*!< usb request code set configuration */
-#define USB_STD_REQ_GET_INTERFACE        10 /*!< usb request code get interface */
-#define USB_STD_REQ_SET_INTERFACE        11 /*!< usb request code set interface */
-#define USB_STD_REQ_SYNCH_FRAME          12 /*!< usb request code synch frame */
-
-/**
-  * @brief usb standard device type
-  */
-#define USB_DESCIPTOR_TYPE_DEVICE        1 /*!< usb standard device type device */
-#define USB_DESCIPTOR_TYPE_CONFIGURATION 2 /*!< usb standard device type configuration */
-#define USB_DESCIPTOR_TYPE_STRING        3 /*!< usb standard device type string */
-#define USB_DESCIPTOR_TYPE_INTERFACE     4 /*!< usb standard device type interface */
-#define USB_DESCIPTOR_TYPE_ENDPOINT      5 /*!< usb standard device type endpoint */
-#define USB_DESCIPTOR_TYPE_DEVICE_QUALIFIER     6 /*!< usb standard device type qualifier */
-#define USB_DESCIPTOR_TYPE_OTHER_SPEED   7 /*!< usb standard device type other speed */
-#define USB_DESCIPTOR_TYPE_INTERFACE_POWER       8 /*!< usb standard device type interface power */
-
-/**
-  * @brief usb standard string type
-  */
-#define  USB_LANGID_STRING               0 /*!< usb standard string type lang id */
-#define  USB_MFC_STRING                  1 /*!< usb standard string type mfc */
-#define  USB_PRODUCT_STRING              2 /*!< usb standard string type product */
-#define  USB_SERIAL_STRING               3 /*!< usb standard string type serial */
-#define  USB_CONFIG_STRING               4 /*!< usb standard string type config */
-#define  USB_INTERFACE_STRING            5 /*!< usb standard string type interface */
-
-/**
-  * @brief usb configuration attributes
-  */
-#define USB_CONF_REMOTE_WAKEUP           2 /*!< usb configuration attributes remote wakeup */
-#define USB_CONF_SELF_POWERED            1 /*!< usb configuration attributes self powered */
-
-/**
-  * @brief usb standard feature selectors
-  */
-#define USB_FEATURE_EPT_HALT             0 /*!< usb standard feature selectors endpoint halt */
-#define USB_FEATURE_REMOTE_WAKEUP        1 /*!< usb standard feature selectors remote wakeup */
-#define USB_FEATURE_TEST_MODE            2 /*!< usb standard feature selectors test mode */
-
-/**
-  * @brief usb device connect state
-  */
-typedef enum
-{
-  USB_CONN_STATE_DEFAULT                =1, /*!< usb device connect state default */
-  USB_CONN_STATE_ADDRESSED,                 /*!< usb device connect state address */
-  USB_CONN_STATE_CONFIGURED,                /*!< usb device connect state configured */
-  USB_CONN_STATE_SUSPENDED                  /*!< usb device connect state suspend */
-}usbd_conn_state; 
-
-/**
-  * @brief endpoint 0 state
-  */
-#define USB_EPT0_IDLE                    0 /*!< usb endpoint state idle */
-#define USB_EPT0_SETUP                   1 /*!< usb endpoint state setup */
-#define USB_EPT0_DATA_IN                 2 /*!< usb endpoint state data in */
-#define USB_EPT0_DATA_OUT                3 /*!< usb endpoint state data out */
-#define USB_EPT0_STATUS_IN               4 /*!< usb endpoint state status in */
-#define USB_EPT0_STATUS_OUT              5 /*!< usb endpoint state status out */
-#define USB_EPT0_STALL                   6 /*!< usb endpoint state stall */ 
-
-/**
-  * @brief usb descriptor length
-  */
-#define USB_DEVICE_QUALIFIER_DESC_LEN    0x0A /*!< usb qualifier descriptor length */
-#define USB_DEVICE_DESC_LEN              0x12 /*!< usb device descriptor length */
-#define USB_DEVICE_CFG_DESC_LEN          0x09 /*!< usb configuration descriptor length */
-#define USB_DEVICE_IF_DESC_LEN           0x09 /*!< usb interface descriptor length */
-#define USB_DEVICE_EPT_LEN               0x07 /*!< usb endpoint descriptor length */
-#define USB_DEVICE_OTG_DESC_LEN          0x03 /*!< usb otg descriptor length */
-#define USB_DEVICE_LANGID_STR_DESC_LEN   0x04 /*!< usb lang id string descriptor length */
-#define USB_DEVICE_OTHER_SPEED_DESC_SIZ_LEN 0x09 /*!< usb other speed descriptor length */
-
-/**
-  * @brief usb class code
-  */
-#define USB_CLASS_CODE_AUDIO             0x01 /*!< usb class code audio */
-#define USB_CLASS_CODE_CDC               0x02 /*!< usb class code cdc */
-#define USB_CLASS_CODE_HID               0x03 /*!< usb class code hid */
-#define USB_CLASS_CODE_PRINTER           0x07 /*!< usb class code printer */
-#define USB_CLASS_CODE_MSC               0x08 /*!< usb class code msc */
-#define USB_CLASS_CODE_HUB               0x09 /*!< usb class code hub */
-#define USB_CLASS_CODE_CDCDATA           0x0A /*!< usb class code cdc data */
-#define USB_CLASS_CODE_CCID              0x0B /*!< usb class code ccid */
-#define USB_CLASS_CODE_VIDEO             0x0E /*!< usb class code video */
-#define USB_CLASS_CODE_VENDOR            0xFF /*!< usb class code vendor */
-
-/**
-  * @brief usb endpoint type
-  */
-#define USB_EPT_DESC_CONTROL             0x00 /*!< usb endpoint description type control */
-#define USB_EPT_DESC_ISO                 0x01 /*!< usb endpoint description type iso */
-#define USB_EPT_DESC_BULK                0x02 /*!< usb endpoint description type bulk */
-#define USB_EPT_DESC_INTERRUPT           0x03 /*!< usb endpoint description type interrupt */
-
-#define USB_EPT_DESC_NSYNC               0x00 /*!< usb endpoint description nsync */
-#define USB_ETP_DESC_ASYNC               0x04 /*!< usb endpoint description async */
-#define USB_ETP_DESC_ADAPTIVE            0x08 /*!< usb endpoint description adaptive */
-#define USB_ETP_DESC_SYNC                0x0C /*!< usb endpoint description sync */
-
-#define USB_EPT_DESC_DATA_EPT            0x00 /*!< usb endpoint description data */
-#define USB_EPT_DESC_FD_EPT              0x10 /*!< usb endpoint description fd */
-#define USB_EPT_DESC_FDDATA_EPT          0x20 /*!< usb endpoint description fddata */
-
-/**
-  * @brief endpoint 0 max size
-  */
-#define USB_MAX_EP0_SIZE                 64 /*!< usb endpoint 0 max size */
-
-/**
-  * @brief usb swap address
-  */
-#define SWAPBYTE(addr)        (uint16_t)(((uint16_t)(*((uint8_t *)(addr)))) + \
-                               (((uint16_t)(*(((uint8_t *)(addr)) + 1))) << 8)) /*!< swap address */
-
-/**
-  * @brief min and max define
-  */
-#define MIN(a, b)  (uint16_t)(((a) < (b)) ? (a) : (b)) /*!< min define*/
-#define MAX(a, b)  (uint16_t)(((a) > (b)) ? (a) : (b)) /*!< max define*/
-
-/**
-  * @brief low byte and high byte define
-  */
-#define LBYTE(x)  ((uint8_t)(x & 0x00FF))        /*!< low byte define */
-#define HBYTE(x)  ((uint8_t)((x & 0xFF00) >>8))  /*!< high byte define*/
-
-/**
-  * @}
-  */
-
-/** @defgroup USB_standard_exported_types
-  * @{
-  */
-  
-/**
-  * @brief usb return status
-  */
-typedef enum
-{
-  USB_OK,              /*!< usb status ok */
-  USB_FAIL,            /*!< usb status fail */
-  USB_WAIT,            /*!< usb status wait */
-  USB_NOT_SUPPORT,     /*!< usb status not support */
-  USB_ERROR,           /*!< usb status error */
-}usb_sts_type;
-
-/** 
-  * @brief format of usb setup data
-  */
-typedef struct
-{
-  uint8_t                                bmRequestType;                 /*!< characteristics of request */
-  uint8_t                                bRequest;                      /*!< specific request */
-  uint16_t                               wValue;                        /*!< word-sized field that varies according to request */
-  uint16_t                               wIndex;                        /*!< word-sized field that varies according to request 
-                                                                           typically used to pass an index or offset */
-  uint16_t                               wLength;                       /*!< number of bytes to transfer if there is a data stage */
-}usb_setup_type;
-
-/** 
-  * @brief format of standard device descriptor
-  */
-typedef struct
-{
-  uint8_t                                bLength;                       /*!< size of this descriptor in bytes */
-  uint8_t                                bDescriptorType;               /*!< device descriptor type */
-  uint16_t                               bcdUSB;                        /*!< usb specification release number */
-  uint8_t                                bDeviceClass;                  /*!< class code (assigned by the usb-if) */
-  uint8_t                                bDeviceSubClass;               /*!< subclass code (assigned by the usb-if) */
-  uint8_t                                bDeviceProtocol;               /*!< protocol code ((assigned by the usb-if)) */
-  uint8_t                                bMaxPacketSize0;               /*!< maximum packet size for endpoint zero */
-  uint16_t                               idVendor;                      /*!< verndor id ((assigned by the usb-if)) */
-  uint16_t                               idProduct;                     /*!< product id ((assigned by the usb-if)) */
-  uint16_t                               bcdDevice;                     /*!< device release number in binary-coded decimal */
-  uint8_t                                iManufacturer;                 /*!< index of string descriptor describing manufacturer */
-  uint8_t                                iProduct;                      /*!< index of string descriptor describing product */
-  uint8_t                                iSerialNumber;                 /*!< index of string descriptor describing serial number */
-  uint8_t                                bNumConfigurations;            /*!< number of possible configurations */
-}usb_device_desc_type;
-
-/** 
-  * @brief format of standard configuration descriptor
-  */
-typedef struct
-{
-  uint8_t                                bLength;                        /*!< size of this descriptor in bytes */
-  uint8_t                                bDescriptorType;                /*!< configuration descriptor type */  
-  uint16_t                               wTotalLength;                   /*!< total length of data returned for this configuration */
-  uint8_t                                bNumInterfaces;                 /*!< number of interfaces supported by this configuration */
-  uint8_t                                bConfigurationValue;            /*!< value to use as an argument to the SetConfiguration() request */
-  uint8_t                                iConfiguration;                 /*!< index of string descriptor describing this configuration */
-  uint8_t                                bmAttributes;                   /*!< configuration characteristics 
-                                                                            D7 reserved
-                                                                            D6 self-powered
-                                                                            D5 remote wakeup
-                                                                            D4~D0 reserved */                                                                    
-  uint8_t                                bMaxPower;                      /*!< maximum power consumption of the usb device from the bus */
-  
-  
-}usb_configuration_desc_type;
-
-/** 
-  * @brief format of standard interface descriptor
-  */
-typedef struct
-{
-  uint8_t                                bLength;                        /*!< size of this descriptor in bytes */
-  uint8_t                                bDescriptorType;                /*!< interface descriptor type */
-  uint8_t                                bInterfaceNumber;               /*!< number of this interface */
-  uint8_t                                bAlternateSetting;              /*!< value used to select this alternate setting for the interface */
-  uint8_t                                bNumEndpoints;                  /*!< number of endpoints used by this interface */        
-  uint8_t                                bInterfaceClass;                /*!< class code (assigned by the usb-if) */
-  uint8_t                                bInterfaceSubClass;             /*!< subclass code (assigned by the usb-if) */
-  uint8_t                                bInterfaceProtocol;             /*!< protocol code (assigned by the usb-if) */
-  uint8_t                                iInterface;                     /*!< index of string descriptor describing this interface */
-}usb_interface_desc_type;
-
-/** 
-  * @brief format of standard endpoint descriptor
-  */
-typedef struct
-{
-  uint8_t                                bLength;                        /*!< size of this descriptor in bytes */
-  uint8_t                                bDescriptorType;                /*!< endpoint descriptor type */
-  uint8_t                                bEndpointAddress;               /*!< the address of the endpoint on the usb device described by this descriptor */
-  uint8_t                                bmAttributes;                   /*!< describes the endpoints attributes when it is configured using bConfiguration value */
-  uint16_t                               wMaxPacketSize;                 /*!< maximum packet size this endpoint */
-  uint8_t                                bInterval;                      /*!< interval for polling endpoint for data transfers */  
-}usb_endpoint_desc_type;
-
-/** 
-  * @brief format of header
-  */
-typedef struct
-{
-  uint8_t                                bLength;                        /*!< size of this descriptor in bytes */
-  uint8_t                                bDescriptorType;                /*!< descriptor type */
-}usb_header_desc_type;
-
-
-/**
-  * @}
-  */
-
-/**
-  * @}
-  */
-  
-/**
-  * @}
-  */
-#ifdef __cplusplus
-}
-#endif
-
-#endif
-
+/**
+  **************************************************************************
+  * @file     usb_std.h
+  * @version  v2.0.6
+  * @date     2021-12-31
+  * @brief    usb standard header file
+  **************************************************************************
+  *                       Copyright notice & Disclaimer
+  *
+  * The software Board Support Package (BSP) that is made available to 
+  * download from Artery official website is the copyrighted work of Artery. 
+  * Artery authorizes customers to use, copy, and distribute the BSP 
+  * software and its related documentation for the purpose of design and 
+  * development in conjunction with Artery microcontrollers. Use of the 
+  * software is governed by this copyright notice and the following disclaimer.
+  *
+  * THIS SOFTWARE IS PROVIDED ON "AS IS" BASIS WITHOUT WARRANTIES,
+  * GUARANTEES OR REPRESENTATIONS OF ANY KIND. ARTERY EXPRESSLY DISCLAIMS,
+  * TO THE FULLEST EXTENT PERMITTED BY LAW, ALL EXPRESS, IMPLIED OR
+  * STATUTORY OR OTHER WARRANTIES, GUARANTEES OR REPRESENTATIONS,
+  * INCLUDING BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
+  * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+  *
+  **************************************************************************
+  */
+
+/* define to prevent recursive inclusion -------------------------------------*/
+#ifndef __USB_STD_H
+#define __USB_STD_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* includes ------------------------------------------------------------------*/
+#include "usb_conf.h"
+
+/** @addtogroup AT32F403A_407_middlewares_usbd_drivers
+  * @{
+  */
+  
+/** @addtogroup USB_standard
+  * @{
+  */
+
+/** @defgroup USB_standard_define
+  * @{
+  */
+
+/**
+  * @brief usb request recipient
+  */
+#define USB_REQ_RECIPIENT_DEVICE         0x00 /*!< usb request recipient device */
+#define USB_REQ_RECIPIENT_INTERFACE      0x01 /*!< usb request recipient interface */
+#define USB_REQ_RECIPIENT_ENDPOINT       0x02 /*!< usb request recipient endpoint */
+#define USB_REQ_RECIPIENT_OTHER          0x03 /*!< usb request recipient other */
+#define USB_REQ_RECIPIENT_MASK           0x1F /*!< usb request recipient mask */
+
+/**
+  * @brief usb request type
+  */
+#define USB_REQ_TYPE_STANDARD            0x00 /*!< usb request type standard */
+#define USB_REQ_TYPE_CLASS               0x20 /*!< usb request type class */
+#define USB_REQ_TYPE_VENDOR              0x40 /*!< usb request type vendor */
+#define USB_REQ_TYPE_RESERVED            0x60 /*!< usb request type reserved */
+
+/**
+  * @brief usb request data transfer direction
+  */
+#define USB_REQ_DIR_HTD                  0x00 /*!< usb request data transfer direction host to device */
+#define USB_REQ_DIR_DTH                  0x80 /*!< usb request data transfer direction device to host */
+
+/**
+  * @brief usb standard device requests codes
+  */
+#define USB_STD_REQ_GET_STATUS           0 /*!< usb request code status */
+#define USB_STD_REQ_CLEAR_FEATURE        1 /*!< usb request code clear feature */
+#define USB_STD_REQ_SET_FEATURE          3 /*!< usb request code feature */
+#define USB_STD_REQ_SET_ADDRESS          5 /*!< usb request code address */
+#define USB_STD_REQ_GET_DESCRIPTOR       6 /*!< usb request code get descriptor */
+#define USB_STD_REQ_SET_DESCRIPTOR       7 /*!< usb request code set descriptor */
+#define USB_STD_REQ_GET_CONFIGURATION    8 /*!< usb request code get configuration */
+#define USB_STD_REQ_SET_CONFIGURATION    9 /*!< usb request code set configuration */
+#define USB_STD_REQ_GET_INTERFACE        10 /*!< usb request code get interface */
+#define USB_STD_REQ_SET_INTERFACE        11 /*!< usb request code set interface */
+#define USB_STD_REQ_SYNCH_FRAME          12 /*!< usb request code synch frame */
+
+/**
+  * @brief usb standard device type
+  */
+#define USB_DESCIPTOR_TYPE_DEVICE        1 /*!< usb standard device type device */
+#define USB_DESCIPTOR_TYPE_CONFIGURATION 2 /*!< usb standard device type configuration */
+#define USB_DESCIPTOR_TYPE_STRING        3 /*!< usb standard device type string */
+#define USB_DESCIPTOR_TYPE_INTERFACE     4 /*!< usb standard device type interface */
+#define USB_DESCIPTOR_TYPE_ENDPOINT      5 /*!< usb standard device type endpoint */
+#define USB_DESCIPTOR_TYPE_DEVICE_QUALIFIER     6 /*!< usb standard device type qualifier */
+#define USB_DESCIPTOR_TYPE_OTHER_SPEED   7 /*!< usb standard device type other speed */
+#define USB_DESCIPTOR_TYPE_INTERFACE_POWER       8 /*!< usb standard device type interface power */
+
+/**
+  * @brief usb standard string type
+  */
+#define  USB_LANGID_STRING               0 /*!< usb standard string type lang id */
+#define  USB_MFC_STRING                  1 /*!< usb standard string type mfc */
+#define  USB_PRODUCT_STRING              2 /*!< usb standard string type product */
+#define  USB_SERIAL_STRING               3 /*!< usb standard string type serial */
+#define  USB_CONFIG_STRING               4 /*!< usb standard string type config */
+#define  USB_INTERFACE_STRING            5 /*!< usb standard string type interface */
+
+/**
+  * @brief usb configuration attributes
+  */
+#define USB_CONF_REMOTE_WAKEUP           2 /*!< usb configuration attributes remote wakeup */
+#define USB_CONF_SELF_POWERED            1 /*!< usb configuration attributes self powered */
+
+/**
+  * @brief usb standard feature selectors
+  */
+#define USB_FEATURE_EPT_HALT             0 /*!< usb standard feature selectors endpoint halt */
+#define USB_FEATURE_REMOTE_WAKEUP        1 /*!< usb standard feature selectors remote wakeup */
+#define USB_FEATURE_TEST_MODE            2 /*!< usb standard feature selectors test mode */
+
+/**
+  * @brief usb device connect state
+  */
+typedef enum
+{
+  USB_CONN_STATE_DEFAULT                =1, /*!< usb device connect state default */
+  USB_CONN_STATE_ADDRESSED,                 /*!< usb device connect state address */
+  USB_CONN_STATE_CONFIGURED,                /*!< usb device connect state configured */
+  USB_CONN_STATE_SUSPENDED                  /*!< usb device connect state suspend */
+}usbd_conn_state; 
+
+/**
+  * @brief endpoint 0 state
+  */
+#define USB_EPT0_IDLE                    0 /*!< usb endpoint state idle */
+#define USB_EPT0_SETUP                   1 /*!< usb endpoint state setup */
+#define USB_EPT0_DATA_IN                 2 /*!< usb endpoint state data in */
+#define USB_EPT0_DATA_OUT                3 /*!< usb endpoint state data out */
+#define USB_EPT0_STATUS_IN               4 /*!< usb endpoint state status in */
+#define USB_EPT0_STATUS_OUT              5 /*!< usb endpoint state status out */
+#define USB_EPT0_STALL                   6 /*!< usb endpoint state stall */ 
+
+/**
+  * @brief usb descriptor length
+  */
+#define USB_DEVICE_QUALIFIER_DESC_LEN    0x0A /*!< usb qualifier descriptor length */
+#define USB_DEVICE_DESC_LEN              0x12 /*!< usb device descriptor length */
+#define USB_DEVICE_CFG_DESC_LEN          0x09 /*!< usb configuration descriptor length */
+#define USB_DEVICE_IF_DESC_LEN           0x09 /*!< usb interface descriptor length */
+#define USB_DEVICE_EPT_LEN               0x07 /*!< usb endpoint descriptor length */
+#define USB_DEVICE_OTG_DESC_LEN          0x03 /*!< usb otg descriptor length */
+#define USB_DEVICE_LANGID_STR_DESC_LEN   0x04 /*!< usb lang id string descriptor length */
+#define USB_DEVICE_OTHER_SPEED_DESC_SIZ_LEN 0x09 /*!< usb other speed descriptor length */
+
+/**
+  * @brief usb class code
+  */
+#define USB_CLASS_CODE_AUDIO             0x01 /*!< usb class code audio */
+#define USB_CLASS_CODE_CDC               0x02 /*!< usb class code cdc */
+#define USB_CLASS_CODE_HID               0x03 /*!< usb class code hid */
+#define USB_CLASS_CODE_PRINTER           0x07 /*!< usb class code printer */
+#define USB_CLASS_CODE_MSC               0x08 /*!< usb class code msc */
+#define USB_CLASS_CODE_HUB               0x09 /*!< usb class code hub */
+#define USB_CLASS_CODE_CDCDATA           0x0A /*!< usb class code cdc data */
+#define USB_CLASS_CODE_CCID              0x0B /*!< usb class code ccid */
+#define USB_CLASS_CODE_VIDEO             0x0E /*!< usb class code video */
+#define USB_CLASS_CODE_VENDOR            0xFF /*!< usb class code vendor */
+
+/**
+  * @brief usb endpoint type
+  */
+#define USB_EPT_DESC_CONTROL             0x00 /*!< usb endpoint description type control */
+#define USB_EPT_DESC_ISO                 0x01 /*!< usb endpoint description type iso */
+#define USB_EPT_DESC_BULK                0x02 /*!< usb endpoint description type bulk */
+#define USB_EPT_DESC_INTERRUPT           0x03 /*!< usb endpoint description type interrupt */
+
+#define USB_EPT_DESC_NSYNC               0x00 /*!< usb endpoint description nsync */
+#define USB_ETP_DESC_ASYNC               0x04 /*!< usb endpoint description async */
+#define USB_ETP_DESC_ADAPTIVE            0x08 /*!< usb endpoint description adaptive */
+#define USB_ETP_DESC_SYNC                0x0C /*!< usb endpoint description sync */
+
+#define USB_EPT_DESC_DATA_EPT            0x00 /*!< usb endpoint description data */
+#define USB_EPT_DESC_FD_EPT              0x10 /*!< usb endpoint description fd */
+#define USB_EPT_DESC_FDDATA_EPT          0x20 /*!< usb endpoint description fddata */
+
+/**
+  * @brief endpoint 0 max size
+  */
+#define USB_MAX_EP0_SIZE                 64 /*!< usb endpoint 0 max size */
+
+/**
+  * @brief usb swap address
+  */
+#define SWAPBYTE(addr)        (uint16_t)(((uint16_t)(*((uint8_t *)(addr)))) + \
+                               (((uint16_t)(*(((uint8_t *)(addr)) + 1))) << 8)) /*!< swap address */
+
+/**
+  * @brief min and max define
+  */
+#define MIN(a, b)  (uint16_t)(((a) < (b)) ? (a) : (b)) /*!< min define*/
+#define MAX(a, b)  (uint16_t)(((a) > (b)) ? (a) : (b)) /*!< max define*/
+
+/**
+  * @brief low byte and high byte define
+  */
+#define LBYTE(x)  ((uint8_t)(x & 0x00FF))        /*!< low byte define */
+#define HBYTE(x)  ((uint8_t)((x & 0xFF00) >>8))  /*!< high byte define*/
+
+/**
+  * @}
+  */
+
+/** @defgroup USB_standard_exported_types
+  * @{
+  */
+  
+/**
+  * @brief usb return status
+  */
+typedef enum
+{
+  USB_OK,              /*!< usb status ok */
+  USB_FAIL,            /*!< usb status fail */
+  USB_WAIT,            /*!< usb status wait */
+  USB_NOT_SUPPORT,     /*!< usb status not support */
+  USB_ERROR,           /*!< usb status error */
+}usb_sts_type;
+
+/** 
+  * @brief format of usb setup data
+  */
+typedef struct
+{
+  uint8_t                                bmRequestType;                 /*!< characteristics of request */
+  uint8_t                                bRequest;                      /*!< specific request */
+  uint16_t                               wValue;                        /*!< word-sized field that varies according to request */
+  uint16_t                               wIndex;                        /*!< word-sized field that varies according to request 
+                                                                           typically used to pass an index or offset */
+  uint16_t                               wLength;                       /*!< number of bytes to transfer if there is a data stage */
+}usb_setup_type;
+
+/** 
+  * @brief format of standard device descriptor
+  */
+typedef struct
+{
+  uint8_t                                bLength;                       /*!< size of this descriptor in bytes */
+  uint8_t                                bDescriptorType;               /*!< device descriptor type */
+  uint16_t                               bcdUSB;                        /*!< usb specification release number */
+  uint8_t                                bDeviceClass;                  /*!< class code (assigned by the usb-if) */
+  uint8_t                                bDeviceSubClass;               /*!< subclass code (assigned by the usb-if) */
+  uint8_t                                bDeviceProtocol;               /*!< protocol code ((assigned by the usb-if)) */
+  uint8_t                                bMaxPacketSize0;               /*!< maximum packet size for endpoint zero */
+  uint16_t                               idVendor;                      /*!< verndor id ((assigned by the usb-if)) */
+  uint16_t                               idProduct;                     /*!< product id ((assigned by the usb-if)) */
+  uint16_t                               bcdDevice;                     /*!< device release number in binary-coded decimal */
+  uint8_t                                iManufacturer;                 /*!< index of string descriptor describing manufacturer */
+  uint8_t                                iProduct;                      /*!< index of string descriptor describing product */
+  uint8_t                                iSerialNumber;                 /*!< index of string descriptor describing serial number */
+  uint8_t                                bNumConfigurations;            /*!< number of possible configurations */
+}usb_device_desc_type;
+
+/** 
+  * @brief format of standard configuration descriptor
+  */
+typedef struct
+{
+  uint8_t                                bLength;                        /*!< size of this descriptor in bytes */
+  uint8_t                                bDescriptorType;                /*!< configuration descriptor type */  
+  uint16_t                               wTotalLength;                   /*!< total length of data returned for this configuration */
+  uint8_t                                bNumInterfaces;                 /*!< number of interfaces supported by this configuration */
+  uint8_t                                bConfigurationValue;            /*!< value to use as an argument to the SetConfiguration() request */
+  uint8_t                                iConfiguration;                 /*!< index of string descriptor describing this configuration */
+  uint8_t                                bmAttributes;                   /*!< configuration characteristics 
+                                                                            D7 reserved
+                                                                            D6 self-powered
+                                                                            D5 remote wakeup
+                                                                            D4~D0 reserved */                                                                    
+  uint8_t                                bMaxPower;                      /*!< maximum power consumption of the usb device from the bus */
+  
+  
+}usb_configuration_desc_type;
+
+/** 
+  * @brief format of standard interface descriptor
+  */
+typedef struct
+{
+  uint8_t                                bLength;                        /*!< size of this descriptor in bytes */
+  uint8_t                                bDescriptorType;                /*!< interface descriptor type */
+  uint8_t                                bInterfaceNumber;               /*!< number of this interface */
+  uint8_t                                bAlternateSetting;              /*!< value used to select this alternate setting for the interface */
+  uint8_t                                bNumEndpoints;                  /*!< number of endpoints used by this interface */        
+  uint8_t                                bInterfaceClass;                /*!< class code (assigned by the usb-if) */
+  uint8_t                                bInterfaceSubClass;             /*!< subclass code (assigned by the usb-if) */
+  uint8_t                                bInterfaceProtocol;             /*!< protocol code (assigned by the usb-if) */
+  uint8_t                                iInterface;                     /*!< index of string descriptor describing this interface */
+}usb_interface_desc_type;
+
+/** 
+  * @brief format of standard endpoint descriptor
+  */
+typedef struct
+{
+  uint8_t                                bLength;                        /*!< size of this descriptor in bytes */
+  uint8_t                                bDescriptorType;                /*!< endpoint descriptor type */
+  uint8_t                                bEndpointAddress;               /*!< the address of the endpoint on the usb device described by this descriptor */
+  uint8_t                                bmAttributes;                   /*!< describes the endpoints attributes when it is configured using bConfiguration value */
+  uint16_t                               wMaxPacketSize;                 /*!< maximum packet size this endpoint */
+  uint8_t                                bInterval;                      /*!< interval for polling endpoint for data transfers */  
+}usb_endpoint_desc_type;
+
+/** 
+  * @brief format of header
+  */
+typedef struct
+{
+  uint8_t                                bLength;                        /*!< size of this descriptor in bytes */
+  uint8_t                                bDescriptorType;                /*!< descriptor type */
+}usb_header_desc_type;
+
+
+/**
+  * @}
+  */
+
+/**
+  * @}
+  */
+  
+/**
+  * @}
+  */
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+

+ 195 - 195
libs/thirdparty/rndis/usbd_drivers/inc/usbd_core.h → libs/artery/usb/inc/usbd_core.h

@@ -1,195 +1,195 @@
-/**
-  **************************************************************************
-  * @file     usbd_core.h
-  * @version  v2.0.6
-  * @date     2021-12-31
-  * @brief    usb device core header file
-  **************************************************************************
-  *                       Copyright notice & Disclaimer
-  *
-  * The software Board Support Package (BSP) that is made available to 
-  * download from Artery official website is the copyrighted work of Artery. 
-  * Artery authorizes customers to use, copy, and distribute the BSP 
-  * software and its related documentation for the purpose of design and 
-  * development in conjunction with Artery microcontrollers. Use of the 
-  * software is governed by this copyright notice and the following disclaimer.
-  *
-  * THIS SOFTWARE IS PROVIDED ON "AS IS" BASIS WITHOUT WARRANTIES,
-  * GUARANTEES OR REPRESENTATIONS OF ANY KIND. ARTERY EXPRESSLY DISCLAIMS,
-  * TO THE FULLEST EXTENT PERMITTED BY LAW, ALL EXPRESS, IMPLIED OR
-  * STATUTORY OR OTHER WARRANTIES, GUARANTEES OR REPRESENTATIONS,
-  * INCLUDING BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
-  * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
-  *
-  **************************************************************************
-  */
-
-/* define to prevent recursive inclusion -------------------------------------*/
-#ifndef __USBD_CORE_H
-#define __USBD_CORE_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-
-#include "usb_conf.h"
-#include "usb_std.h"
-
-/** @addtogroup AT32F403A_407_middlewares_usbd_drivers
-  * @{
-  */
-  
-/** @addtogroup USBD_drivers_core
-  * @{
-  */
-
-/** @defgroup USBD_core_exported_types
-  * @{
-  */
-
-/**
-  * @brief usb device event
-  */
-typedef enum
-{
-  USBD_NOP_EVENT,      /*!< usb device event nop */
-  USBD_RESET_EVENT,    /*!< usb device event reset */
-  USBD_SUSPEND_EVENT,  /*!< usb device event suspend */
-  USBD_WAKEUP_EVENT,   /*!< usb device event wakeup */
-  USBD_ERR_EVENT       /*!< usb device event error */
-}usbd_event_type;
-
-/**
-  * @brief usb device descriptor struct 
-  */
-typedef struct
-{
-  uint16_t length;                       /*!< descriptor length */
-  uint8_t *descriptor;                   /*!< descriptor string */
-}usbd_desc_t;
-
-/**
-  * @brief usb device descriptor handler 
-  */
-typedef struct
-{
-  usbd_desc_t *(*get_device_descriptor)(void);                       /*!< get device descriptor callback */
-  usbd_desc_t *(*get_device_qualifier)(void);                        /*!< get device qualifier callback */
-  usbd_desc_t *(*get_device_configuration)(void);                    /*!< get device configuration callback */
-  usbd_desc_t *(*get_device_other_speed)(void);                      /*!< get device other speed callback */
-  usbd_desc_t *(*get_device_lang_id)(void);                          /*!< get device lang id callback */
-  usbd_desc_t *(*get_device_manufacturer_string)(void);              /*!< get device manufacturer callback */
-  usbd_desc_t *(*get_device_product_string)(void);                   /*!< get device product callback */
-  usbd_desc_t *(*get_device_serial_string)(void);                    /*!< get device serial callback */
-  usbd_desc_t *(*get_device_interface_string)(void);                 /*!< get device interface string callback */
-  usbd_desc_t *(*get_device_config_string)(void);                    /*!< get device device config callback */
-}usbd_desc_handler;
-
-/**
-  * @brief usb device class handler
-  */
-typedef struct
-{
-  usb_sts_type (*init_handler)(void *udev);                          /*!< usb class init handler */
-  usb_sts_type (*clear_handler)(void *udev);                         /*!< usb class clear handler */
-  usb_sts_type (*setup_handler)(void *udev, usb_setup_type *setup);  /*!< usb class setup handler */
-  usb_sts_type (*ept0_tx_handler)(void *udev);                       /*!< usb class endpoint 0 tx complete handler */
-  usb_sts_type (*ept0_rx_handler)(void *udev);                       /*!< usb class endpoint 0 rx complete handler */
-  usb_sts_type (*in_handler)(void *udev, uint8_t ept_num);           /*!< usb class in transfer complete handler */
-  usb_sts_type (*out_handler)(void *udev, uint8_t ept_num);          /*!< usb class out transfer complete handler */
-  usb_sts_type (*sof_handler)(void *udev);                           /*!< usb class sof handler */
-  usb_sts_type (*event_handler)(void *udev, usbd_event_type event);  /*!< usb class event handler */
-  usb_sts_type (*iso_in_incomplete_handler)(void *udev);                           
-  usb_sts_type (*iso_out_incomplete_handler)(void *udev);
-  uint8_t *(*get_cfg_descriptor)(uint8_t speed, uint16_t *length);
-}usbd_class_handler;
-
-/**
-  * @brief usb device core struct type
-  */
-typedef struct
-{
-  usb_reg_type *usb_reg;                 /*!< usb register pointer */
-  
-  usbd_class_handler *class_handler;     /*!< usb device class handler pointer */
-  usbd_desc_handler *desc_handler;       /*!< usb device descriptor handler pointer */
-  
-  usb_ept_info ept_in[USB_EPT_MAX_NUM];  /*!< usb in endpoint infomation struct */
-  usb_ept_info ept_out[USB_EPT_MAX_NUM]; /*!< usb out endpoint infomation struct */
-  
-  usb_setup_type setup;                  /*!< usb setup type struct */
-  uint8_t setup_buffer[12];              /*!< usb setup request buffer */
-  
-  uint8_t ept0_sts;                      /*!< usb control endpoint 0 state */
-  uint8_t speed;                         /*!< usb speed */
-  uint16_t ept0_wlength;                 /*!< usb endpoint 0 transfer length */
-  
-  usbd_conn_state conn_state;            /*!< usb current connect state */
-  usbd_conn_state old_conn_state;        /*!< usb save the previous connect state */
-  
-  uint8_t device_addr;                   /*!< device address */
-  uint8_t remote_wakup;                  /*!< remote wakeup state */
-  uint8_t default_config;                /*!< usb default config state */
-  uint8_t dev_config;                    /*!< usb device config state */
-  uint16_t config_status;                /*!< usb configure status */
-}usbd_core_type;
-
-/**
-  * @}
-  */ 
-
-/** @defgroup USBD_core_exported_functions
-  * @{
-  */
-void usbd_core_in_handler(usbd_core_type *udev, uint8_t ept_num);
-void usbd_core_out_handler(usbd_core_type *udev, uint8_t ept_num);
-void usbd_core_setup_handler(usbd_core_type *udev, uint8_t ept_num);
-void usbd_ctrl_unsupport(usbd_core_type *udev);
-void usbd_ctrl_send(usbd_core_type *udev, uint8_t *buffer, uint16_t len);
-void usbd_ctrl_recv(usbd_core_type *udev, uint8_t *buffer, uint16_t len);
-void usbd_ctrl_send_status(usbd_core_type *udev);
-void usbd_ctrl_recv_status(usbd_core_type *udev);
-void usbd_set_stall(usbd_core_type *udev, uint8_t ept_addr);
-void usbd_clear_stall(usbd_core_type *udev, uint8_t ept_addr);
-void usbd_ept_open(usbd_core_type *udev, uint8_t ept_addr, uint8_t ept_type, uint16_t maxpacket);
-void usbd_ept_close(usbd_core_type *udev, uint8_t ept_addr);
-void usbd_ept_send(usbd_core_type *udev, uint8_t ept_num, uint8_t *buffer, uint16_t len);
-void usbd_ept_recv(usbd_core_type *udev, uint8_t ept_num, uint8_t *buffer, uint16_t len);
-void usbd_connect(usbd_core_type *udev);
-void usbd_disconnect(usbd_core_type *udev);
-void usbd_set_device_addr(usbd_core_type *udev, uint8_t address);
-uint32_t usbd_get_recv_len(usbd_core_type *udev, uint8_t ept_addr);
-usbd_conn_state usbd_connect_state_get(usbd_core_type *udev);
-void usbd_ept_dbuffer_enable(usbd_core_type *udev, uint8_t ept_addr);
-void usbd_ept_buf_auto_define(usb_ept_info *ept_info);
-void usbd_ept_buf_custom_define( usbd_core_type *udev, uint8_t ept_addr, 
-                                 uint32_t addr);
-void usbd_ept_defaut_init(usbd_core_type *udev);
-void usbd_remote_wakeup(usbd_core_type *udev);
-void usbd_enter_suspend(usbd_core_type *udev);
-void usbd_core_init(usbd_core_type *udev,
-                    usb_reg_type *usb_reg,
-                    usbd_class_handler *dev_handler, 
-                    usbd_desc_handler *desc_handler,
-                    uint8_t core_id);
-
-
-/**
-  * @}
-  */ 
-
-/**
-  * @}
-  */ 
-
-/**
-  * @}
-  */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
-
+/**
+  **************************************************************************
+  * @file     usbd_core.h
+  * @version  v2.0.6
+  * @date     2021-12-31
+  * @brief    usb device core header file
+  **************************************************************************
+  *                       Copyright notice & Disclaimer
+  *
+  * The software Board Support Package (BSP) that is made available to 
+  * download from Artery official website is the copyrighted work of Artery. 
+  * Artery authorizes customers to use, copy, and distribute the BSP 
+  * software and its related documentation for the purpose of design and 
+  * development in conjunction with Artery microcontrollers. Use of the 
+  * software is governed by this copyright notice and the following disclaimer.
+  *
+  * THIS SOFTWARE IS PROVIDED ON "AS IS" BASIS WITHOUT WARRANTIES,
+  * GUARANTEES OR REPRESENTATIONS OF ANY KIND. ARTERY EXPRESSLY DISCLAIMS,
+  * TO THE FULLEST EXTENT PERMITTED BY LAW, ALL EXPRESS, IMPLIED OR
+  * STATUTORY OR OTHER WARRANTIES, GUARANTEES OR REPRESENTATIONS,
+  * INCLUDING BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
+  * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+  *
+  **************************************************************************
+  */
+
+/* define to prevent recursive inclusion -------------------------------------*/
+#ifndef __USBD_CORE_H
+#define __USBD_CORE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#include "usb_conf.h"
+#include "usb_std.h"
+
+/** @addtogroup AT32F403A_407_middlewares_usbd_drivers
+  * @{
+  */
+  
+/** @addtogroup USBD_drivers_core
+  * @{
+  */
+
+/** @defgroup USBD_core_exported_types
+  * @{
+  */
+
+/**
+  * @brief usb device event
+  */
+typedef enum
+{
+  USBD_NOP_EVENT,      /*!< usb device event nop */
+  USBD_RESET_EVENT,    /*!< usb device event reset */
+  USBD_SUSPEND_EVENT,  /*!< usb device event suspend */
+  USBD_WAKEUP_EVENT,   /*!< usb device event wakeup */
+  USBD_ERR_EVENT       /*!< usb device event error */
+}usbd_event_type;
+
+/**
+  * @brief usb device descriptor struct 
+  */
+typedef struct
+{
+  uint16_t length;                       /*!< descriptor length */
+  uint8_t *descriptor;                   /*!< descriptor string */
+}usbd_desc_t;
+
+/**
+  * @brief usb device descriptor handler 
+  */
+typedef struct
+{
+  usbd_desc_t *(*get_device_descriptor)(void);                       /*!< get device descriptor callback */
+  usbd_desc_t *(*get_device_qualifier)(void);                        /*!< get device qualifier callback */
+  usbd_desc_t *(*get_device_configuration)(void);                    /*!< get device configuration callback */
+  usbd_desc_t *(*get_device_other_speed)(void);                      /*!< get device other speed callback */
+  usbd_desc_t *(*get_device_lang_id)(void);                          /*!< get device lang id callback */
+  usbd_desc_t *(*get_device_manufacturer_string)(void);              /*!< get device manufacturer callback */
+  usbd_desc_t *(*get_device_product_string)(void);                   /*!< get device product callback */
+  usbd_desc_t *(*get_device_serial_string)(void);                    /*!< get device serial callback */
+  usbd_desc_t *(*get_device_interface_string)(void);                 /*!< get device interface string callback */
+  usbd_desc_t *(*get_device_config_string)(void);                    /*!< get device device config callback */
+}usbd_desc_handler;
+
+/**
+  * @brief usb device class handler
+  */
+typedef struct
+{
+  uint8_t (*init_handler)(void *udev);                          /*!< usb class init handler */
+  uint8_t (*clear_handler)(void *udev);                         /*!< usb class clear handler */
+  uint8_t (*setup_handler)(void *udev, usb_setup_type *setup);  /*!< usb class setup handler */
+  uint8_t (*ept0_tx_handler)(void *udev);                       /*!< usb class endpoint 0 tx complete handler */
+  uint8_t (*ept0_rx_handler)(void *udev);                       /*!< usb class endpoint 0 rx complete handler */
+  uint8_t (*in_handler)(void *udev, uint8_t ept_num);           /*!< usb class in transfer complete handler */
+  uint8_t (*out_handler)(void *udev, uint8_t ept_num);          /*!< usb class out transfer complete handler */
+  uint8_t (*sof_handler)(void *udev);                           /*!< usb class sof handler */
+  uint8_t (*event_handler)(void *udev, usbd_event_type event);  /*!< usb class event handler */
+  uint8_t (*iso_in_incomplete_handler)(void *udev);                           
+  uint8_t (*iso_out_incomplete_handler)(void *udev);
+  uint8_t *(*get_cfg_descriptor)(uint8_t speed, uint16_t *length);
+}usbd_class_handler;
+
+/**
+  * @brief usb device core struct type
+  */
+typedef struct
+{
+  usb_reg_type *usb_reg;                 /*!< usb register pointer */
+  
+  usbd_class_handler *class_handler;     /*!< usb device class handler pointer */
+  usbd_desc_handler *desc_handler;       /*!< usb device descriptor handler pointer */
+  
+  usb_ept_info ept_in[USB_EPT_MAX_NUM];  /*!< usb in endpoint infomation struct */
+  usb_ept_info ept_out[USB_EPT_MAX_NUM]; /*!< usb out endpoint infomation struct */
+  
+  usb_setup_type setup;                  /*!< usb setup type struct */
+  uint8_t setup_buffer[12];              /*!< usb setup request buffer */
+  
+  uint8_t ept0_sts;                      /*!< usb control endpoint 0 state */
+  uint8_t speed;                         /*!< usb speed */
+  uint16_t ept0_wlength;                 /*!< usb endpoint 0 transfer length */
+  
+  usbd_conn_state conn_state;            /*!< usb current connect state */
+  usbd_conn_state old_conn_state;        /*!< usb save the previous connect state */
+  
+  uint8_t device_addr;                   /*!< device address */
+  uint8_t remote_wakup;                  /*!< remote wakeup state */
+  uint8_t default_config;                /*!< usb default config state */
+  uint8_t dev_config;                    /*!< usb device config state */
+  uint16_t config_status;                /*!< usb configure status */
+}usbd_core_type;
+
+/**
+  * @}
+  */ 
+
+/** @defgroup USBD_core_exported_functions
+  * @{
+  */
+void usbd_core_in_handler(usbd_core_type *udev, uint8_t ept_num);
+void usbd_core_out_handler(usbd_core_type *udev, uint8_t ept_num);
+void usbd_core_setup_handler(usbd_core_type *udev, uint8_t ept_num);
+void usbd_ctrl_unsupport(usbd_core_type *udev);
+void usbd_ctrl_send(usbd_core_type *udev, uint8_t *buffer, uint16_t len);
+void usbd_ctrl_recv(usbd_core_type *udev, uint8_t *buffer, uint16_t len);
+void usbd_ctrl_send_status(usbd_core_type *udev);
+void usbd_ctrl_recv_status(usbd_core_type *udev);
+void usbd_set_stall(usbd_core_type *udev, uint8_t ept_addr);
+void usbd_clear_stall(usbd_core_type *udev, uint8_t ept_addr);
+void usbd_ept_open(usbd_core_type *udev, uint8_t ept_addr, uint8_t ept_type, uint16_t maxpacket);
+void usbd_ept_close(usbd_core_type *udev, uint8_t ept_addr);
+void usbd_ept_send(usbd_core_type *udev, uint8_t ept_num, uint8_t *buffer, uint16_t len);
+void usbd_ept_recv(usbd_core_type *udev, uint8_t ept_num, uint8_t *buffer, uint16_t len);
+void usbd_connect(usbd_core_type *udev);
+void usbd_disconnect(usbd_core_type *udev);
+void usbd_set_device_addr(usbd_core_type *udev, uint8_t address);
+uint32_t usbd_get_recv_len(usbd_core_type *udev, uint8_t ept_addr);
+usbd_conn_state usbd_connect_state_get(usbd_core_type *udev);
+void usbd_ept_dbuffer_enable(usbd_core_type *udev, uint8_t ept_addr);
+void usbd_ept_buf_auto_define(usb_ept_info *ept_info);
+void usbd_ept_buf_custom_define( usbd_core_type *udev, uint8_t ept_addr, 
+                                 uint32_t addr);
+void usbd_ept_defaut_init(usbd_core_type *udev);
+void usbd_remote_wakeup(usbd_core_type *udev);
+void usbd_enter_suspend(usbd_core_type *udev);
+void usbd_core_init(usbd_core_type *udev,
+                    usb_reg_type *usb_reg,
+                    usbd_class_handler *dev_handler, 
+                    usbd_desc_handler *desc_handler,
+                    uint8_t core_id);
+
+
+/**
+  * @}
+  */ 
+
+/**
+  * @}
+  */ 
+
+/**
+  * @}
+  */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+

+ 74 - 74
libs/thirdparty/rndis/usbd_drivers/inc/usbd_int.h → libs/artery/usb/inc/usbd_int.h

@@ -1,74 +1,74 @@
-/**
-  **************************************************************************
-  * @file     usb_int.h
-  * @version  v2.1.2
-  * @date     2022-08-16
-  * @brief    usb header file
-  **************************************************************************
-  *                       Copyright notice & Disclaimer
-  *
-  * The software Board Support Package (BSP) that is made available to
-  * download from Artery official website is the copyrighted work of Artery.
-  * Artery authorizes customers to use, copy, and distribute the BSP
-  * software and its related documentation for the purpose of design and
-  * development in conjunction with Artery microcontrollers. Use of the
-  * software is governed by this copyright notice and the following disclaimer.
-  *
-  * THIS SOFTWARE IS PROVIDED ON "AS IS" BASIS WITHOUT WARRANTIES,
-  * GUARANTEES OR REPRESENTATIONS OF ANY KIND. ARTERY EXPRESSLY DISCLAIMS,
-  * TO THE FULLEST EXTENT PERMITTED BY LAW, ALL EXPRESS, IMPLIED OR
-  * STATUTORY OR OTHER WARRANTIES, GUARANTEES OR REPRESENTATIONS,
-  * INCLUDING BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
-  * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
-  *
-  **************************************************************************
-  */
-
-/* define to prevent recursive inclusion -------------------------------------*/
-#ifndef __USB_INT_H
-#define __USB_INT_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-
-/* includes ------------------------------------------------------------------*/
-#include "usbd_core.h"
-
-/** @addtogroup AT32F403A_407_middlewares_usbd_drivers
-  * @{
-  */
-
-/** @addtogroup USBD_drivers_interrupt
-  * @{
-  */
-
-/** @defgroup USBD_int_exported_functions
-  * @{
-  */
-void usbd_irq_handler(usbd_core_type *udev);
-void usbd_ept_loop_handler(usbd_core_type *udev);
-void usbd_eptn_handler(usbd_core_type *udev, usb_ept_number_type ept_num);
-void usbd_reset_handler(usbd_core_type *udev);
-void usbd_sof_handler(usbd_core_type *udev);
-void usbd_suspend_handler(usbd_core_type *udev);
-void usbd_wakeup_handler(usbd_core_type *udev);
-
-/**
-  * @}
-  */
-
-/**
-  * @}
-  */
-
-/**
-  * @}
-  */
-
-#ifdef __cplusplus
-}
-#endif
-#endif
-
+/**
+  **************************************************************************
+  * @file     usb_int.h
+  * @version  v2.0.6
+  * @date     2021-12-31
+  * @brief    usb header file
+  **************************************************************************
+  *                       Copyright notice & Disclaimer
+  *
+  * The software Board Support Package (BSP) that is made available to 
+  * download from Artery official website is the copyrighted work of Artery. 
+  * Artery authorizes customers to use, copy, and distribute the BSP 
+  * software and its related documentation for the purpose of design and 
+  * development in conjunction with Artery microcontrollers. Use of the 
+  * software is governed by this copyright notice and the following disclaimer.
+  *
+  * THIS SOFTWARE IS PROVIDED ON "AS IS" BASIS WITHOUT WARRANTIES,
+  * GUARANTEES OR REPRESENTATIONS OF ANY KIND. ARTERY EXPRESSLY DISCLAIMS,
+  * TO THE FULLEST EXTENT PERMITTED BY LAW, ALL EXPRESS, IMPLIED OR
+  * STATUTORY OR OTHER WARRANTIES, GUARANTEES OR REPRESENTATIONS,
+  * INCLUDING BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
+  * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+  *
+  **************************************************************************
+  */
+
+/* define to prevent recursive inclusion -------------------------------------*/
+#ifndef __USB_INT_H
+#define __USB_INT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* includes ------------------------------------------------------------------*/
+#include "usbd_core.h"
+
+/** @addtogroup AT32F403A_407_middlewares_usbd_drivers
+  * @{
+  */
+  
+/** @addtogroup USBD_drivers_interrupt
+  * @{
+  */
+  
+/** @defgroup USBD_int_exported_functions
+  * @{
+  */
+void usbd_irq_handler(usbd_core_type *udev);
+void usbd_ept_loop_handler(usbd_core_type *udev);
+void usbd_eptn_handler(usbd_core_type *udev, usb_ept_number_type ept_num);
+void usbd_reset_handler(usbd_core_type *udev);
+void usbd_sof_handler(usbd_core_type *udev);
+void usbd_suspend_handler(usbd_core_type *udev);
+void usbd_wakeup_handler(usbd_core_type *udev);
+
+/**
+  * @}
+  */ 
+  
+/**
+  * @}
+  */ 
+
+/**
+  * @}
+  */
+
+#ifdef __cplusplus
+}
+#endif
+#endif
+

+ 74 - 74
libs/thirdparty/rndis/usbd_drivers/inc/usbd_sdr.h → libs/artery/usb/inc/usbd_sdr.h

@@ -1,74 +1,74 @@
-/**
-  **************************************************************************
-  * @file     usbd_sdr.h
-  * @version  v2.0.6
-  * @date     2021-12-31
-  * @brief    usb standard request header file
-  **************************************************************************
-  *                       Copyright notice & Disclaimer
-  *
-  * The software Board Support Package (BSP) that is made available to 
-  * download from Artery official website is the copyrighted work of Artery. 
-  * Artery authorizes customers to use, copy, and distribute the BSP 
-  * software and its related documentation for the purpose of design and 
-  * development in conjunction with Artery microcontrollers. Use of the 
-  * software is governed by this copyright notice and the following disclaimer.
-  *
-  * THIS SOFTWARE IS PROVIDED ON "AS IS" BASIS WITHOUT WARRANTIES,
-  * GUARANTEES OR REPRESENTATIONS OF ANY KIND. ARTERY EXPRESSLY DISCLAIMS,
-  * TO THE FULLEST EXTENT PERMITTED BY LAW, ALL EXPRESS, IMPLIED OR
-  * STATUTORY OR OTHER WARRANTIES, GUARANTEES OR REPRESENTATIONS,
-  * INCLUDING BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
-  * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
-  *
-  **************************************************************************
-  */
-
-/* define to prevent recursive inclusion -------------------------------------*/
-#ifndef __USBD_SDR_H
-#define __USBD_SDR_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-
-/* includes ------------------------------------------------------------------*/
-#include "usbd_core.h"
-
-/** @addtogroup AT32F403A_407_middlewares_usbd_drivers
-  * @{
-  */
-  
-/** @addtogroup USBD_drivers_standard_request
-  * @{
-  */
-
-/** @defgroup USBD_sdr_exported_functions
-  * @{
-  */
-  
-void usbd_setup_request_parse(usb_setup_type *setup, uint8_t *buf);
-usb_sts_type usbd_device_request(usbd_core_type *udev);
-usb_sts_type usbd_interface_request(usbd_core_type *udev);
-usb_sts_type usbd_endpoint_request(usbd_core_type *udev);
-
-
-/**
-  * @}
-  */ 
-  
-/**
-  * @}
-  */ 
-
-/**
-  * @}
-  */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
-
+/**
+  **************************************************************************
+  * @file     usbd_sdr.h
+  * @version  v2.0.6
+  * @date     2021-12-31
+  * @brief    usb standard request header file
+  **************************************************************************
+  *                       Copyright notice & Disclaimer
+  *
+  * The software Board Support Package (BSP) that is made available to 
+  * download from Artery official website is the copyrighted work of Artery. 
+  * Artery authorizes customers to use, copy, and distribute the BSP 
+  * software and its related documentation for the purpose of design and 
+  * development in conjunction with Artery microcontrollers. Use of the 
+  * software is governed by this copyright notice and the following disclaimer.
+  *
+  * THIS SOFTWARE IS PROVIDED ON "AS IS" BASIS WITHOUT WARRANTIES,
+  * GUARANTEES OR REPRESENTATIONS OF ANY KIND. ARTERY EXPRESSLY DISCLAIMS,
+  * TO THE FULLEST EXTENT PERMITTED BY LAW, ALL EXPRESS, IMPLIED OR
+  * STATUTORY OR OTHER WARRANTIES, GUARANTEES OR REPRESENTATIONS,
+  * INCLUDING BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
+  * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+  *
+  **************************************************************************
+  */
+
+/* define to prevent recursive inclusion -------------------------------------*/
+#ifndef __USBD_SDR_H
+#define __USBD_SDR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* includes ------------------------------------------------------------------*/
+#include "usbd_core.h"
+
+/** @addtogroup AT32F403A_407_middlewares_usbd_drivers
+  * @{
+  */
+  
+/** @addtogroup USBD_drivers_standard_request
+  * @{
+  */
+
+/** @defgroup USBD_sdr_exported_functions
+  * @{
+  */
+  
+void usbd_setup_request_parse(usb_setup_type *setup, uint8_t *buf);
+usb_sts_type usbd_device_request(usbd_core_type *udev);
+usb_sts_type usbd_interface_request(usbd_core_type *udev);
+usb_sts_type usbd_endpoint_request(usbd_core_type *udev);
+
+
+/**
+  * @}
+  */ 
+  
+/**
+  * @}
+  */ 
+
+/**
+  * @}
+  */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+

+ 705 - 711
libs/thirdparty/rndis/usbd_drivers/src/usbd_core.c → libs/artery/usb/src/usbd_core.c

@@ -1,711 +1,705 @@
-/**
-  **************************************************************************
-  * @file     usbd_core.c
-  * @version  v2.1.2
-  * @date     2022-08-16
-  * @brief    usb driver
-  **************************************************************************
-  *                       Copyright notice & Disclaimer
-  *
-  * The software Board Support Package (BSP) that is made available to
-  * download from Artery official website is the copyrighted work of Artery.
-  * Artery authorizes customers to use, copy, and distribute the BSP
-  * software and its related documentation for the purpose of design and
-  * development in conjunction with Artery microcontrollers. Use of the
-  * software is governed by this copyright notice and the following disclaimer.
-  *
-  * THIS SOFTWARE IS PROVIDED ON "AS IS" BASIS WITHOUT WARRANTIES,
-  * GUARANTEES OR REPRESENTATIONS OF ANY KIND. ARTERY EXPRESSLY DISCLAIMS,
-  * TO THE FULLEST EXTENT PERMITTED BY LAW, ALL EXPRESS, IMPLIED OR
-  * STATUTORY OR OTHER WARRANTIES, GUARANTEES OR REPRESENTATIONS,
-  * INCLUDING BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
-  * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
-  *
-  **************************************************************************
-  */
-#include "usbd_core.h"
-#include "usbd_sdr.h"
-
-/** @addtogroup AT32F403A_407_middlewares_usbd_drivers
-  * @{
-  */
-
-/** @defgroup USBD_drivers_core
-  * @brief usb device drivers core
-  * @{
-  */
-
-/** @defgroup USBD_core_private_functions
-  * @{
-  */
-
-/**
-  * @brief  usb core in transfer complete handler
-  * @param  udev: to the structure of usbd_core_type
-  * @param  ept_addr: endpoint number
-  * @retval none
-  */
-void usbd_core_in_handler(usbd_core_type *udev, uint8_t ept_addr)
-{
-  /* get endpoint info*/
-  usb_ept_info *ept_info = &udev->ept_in[ept_addr & 0x7F];
-
-  if(ept_addr == 0)
-  {
-    if(udev->ept0_sts == USB_EPT0_DATA_IN)
-    {
-      if(ept_info->rem0_len > ept_info->maxpacket)
-      {
-        ept_info->rem0_len -= ept_info->maxpacket;
-        usbd_ept_send(udev, 0, ept_info->trans_buf,
-                      MIN(ept_info->rem0_len, ept_info->maxpacket));
-      }
-      /* endpoint 0 */
-      else if(ept_info->last_len == ept_info->maxpacket
-        && ept_info->ept0_slen >= ept_info->maxpacket
-        && ept_info->ept0_slen < udev->ept0_wlength)
-      {
-        ept_info->last_len = 0;
-        usbd_ept_send(udev, 0, 0, 0);
-        usbd_ept_recv(udev, ept_addr, 0, 0);
-      }
-      else
-      {
-
-        if(udev->class_handler->ept0_tx_handler != 0 &&
-            udev->conn_state == USB_CONN_STATE_CONFIGURED)
-        {
-          udev->class_handler->ept0_tx_handler(udev);
-        }
-        usbd_ctrl_recv_status(udev);
-
-      }
-    }
-  }
-  else if(udev->class_handler->in_handler != 0 &&
-          udev->conn_state == USB_CONN_STATE_CONFIGURED)
-  {
-    /* other user define endpoint */
-    udev->class_handler->in_handler(udev, ept_addr);
-  }
-}
-
-/**
-  * @brief  usb core out transfer complete handler
-  * @param  udev: to the structure of usbd_core_type
-  * @param  ept_addr: endpoint number
-  * @retval none
-  */
-void usbd_core_out_handler(usbd_core_type *udev, uint8_t ept_addr)
-{
-   /* get endpoint info*/
-  usb_ept_info *ept_info = &udev->ept_out[ept_addr & 0x7F];
-
-  if(ept_addr == 0)
-  {
-    /* endpoint 0 */
-    if(udev->ept0_sts == USB_EPT0_DATA_OUT)
-    {
-      if(ept_info->rem0_len > ept_info->maxpacket)
-      {
-        ept_info->rem0_len -= ept_info->maxpacket;
-        usbd_ept_recv(udev, ept_addr, ept_info->trans_buf,
-                      MIN(ept_info->rem0_len, ept_info->maxpacket));
-      }
-      else
-      {
-          if(udev->class_handler->ept0_rx_handler != 0)
-          {
-            udev->class_handler->ept0_rx_handler(udev);
-          }
-          usbd_ctrl_send_status(udev);
-      }
-    }
-  }
-  else if(udev->class_handler->out_handler != 0 &&
-          udev->conn_state == USB_CONN_STATE_CONFIGURED)
-  {
-    /* other user define endpoint */
-    udev->class_handler->out_handler(udev, ept_addr);
-  }
-}
-
-/**
-  * @brief  usb core setup transfer complete handler
-  * @param  udev: to the structure of usbd_core_type
-  * @param  ept_addr: endpoint number
-  * @retval none
-  */
-void usbd_core_setup_handler(usbd_core_type *udev, uint8_t ept_num)
-{
-  /* setup parse */
-  usbd_setup_request_parse(&udev->setup, udev->setup_buffer);
-
-  /* set ept0 status */
-  udev->ept0_sts = USB_EPT0_SETUP;
-  udev->ept0_wlength = udev->setup.wLength;
-
-  switch(udev->setup.bmRequestType & USB_REQ_RECIPIENT_MASK)
-  {
-    case USB_REQ_RECIPIENT_DEVICE:
-      /* recipient device request */
-      usbd_device_request(udev);
-      break;
-    case USB_REQ_RECIPIENT_INTERFACE:
-      /* recipient interface request */
-      usbd_interface_request(udev);
-      break;
-    case USB_REQ_RECIPIENT_ENDPOINT:
-      /* recipient endpoint request */
-      usbd_endpoint_request(udev);
-      break;
-    default:
-      break;
-  }
-}
-
-/**
-  * @brief  usb control endpoint send data
-  * @param  udev: to the structure of usbd_core_type
-  * @param  ept_addr: endpoint number
-  * @param  buffer: send data buffer
-  * @param  len: send data length
-  * @retval none
-  */
-void usbd_ctrl_send(usbd_core_type *udev, uint8_t *buffer, uint16_t len)
-{
-  usb_ept_info *ept_info = &udev->ept_in[0];
-
-  ept_info->ept0_slen = len;
-  ept_info->rem0_len = len;
-  udev->ept0_sts = USB_EPT0_DATA_IN;
-
-  usbd_ept_send(udev, 0, buffer, len);
-}
-
-/**
-  * @brief  usb control endpoint receive data
-  * @param  udev: to the structure of usbd_core_type
-  * @param  ept_addr: endpoint number
-  * @param  buffer: recv data buffer
-  * @param  len: recv data length
-  * @retval none
-  */
-void usbd_ctrl_recv(usbd_core_type *udev, uint8_t *buffer, uint16_t len)
-{
-  usb_ept_info *ept_info = &udev->ept_out[0];
-
-  ept_info->ept0_slen = len;
-  ept_info->rem0_len = len;
-  udev->ept0_sts = USB_EPT0_DATA_OUT;
-
-  usbd_ept_recv(udev, 0, buffer, len);
-}
-
-/**
-  * @brief  usb control endpoint send in status
-  * @param  udev: to the structure of usbd_core_type
-  * @retval none
-  */
-void usbd_ctrl_send_status(usbd_core_type *udev)
-{
-  udev->ept0_sts = USB_EPT0_STATUS_IN;
-
-  usbd_ept_send(udev, 0, 0, 0);
-}
-
-/**
-  * @brief  usb control endpoint send out status
-  * @param  udev: usb device core handler type
-  * @retval none
-  */
-void usbd_ctrl_recv_status(usbd_core_type *udev)
-{
-  udev->ept0_sts = USB_EPT0_STATUS_OUT;
-
-  usbd_ept_recv(udev, 0, 0, 0);
-}
-
-/**
-  * @brief  clear endpoint stall
-  * @param  udev: to the structure of usbd_core_type
-  * @param  ept_addr: endpoint number
-  * @retval none
-  */
-void usbd_clear_stall(usbd_core_type *udev, uint8_t ept_addr)
-{
-  usb_ept_info *ept_info;
-
-  if(ept_addr & 0x80)
-  {
-    /* in endpoint */
-    ept_info = &udev->ept_in[ept_addr & 0x7F];
-    USB_SET_TXSTS(ept_info->eptn, USB_TX_VALID);
-  }
-  else
-  {
-    /* out endpoint */
-    ept_info = &udev->ept_out[ept_addr & 0x7F];
-    USB_SET_RXSTS(ept_info->eptn, USB_RX_VALID);
-  }
-  ept_info->stall = 0;
-}
-
-/**
-  * @brief  usb set endpoint to stall.
-  * @param  udev: to the structure of usbd_core_type
-  * @param  ept_addr: endpoint number
-  * @retval none
-  */
-void usbd_set_stall(usbd_core_type *udev, uint8_t ept_addr)
-{
-  usb_ept_info *ept_info;
-
-  if(ept_addr & 0x80)
-  {
-    /* in endpoint */
-    ept_info = &udev->ept_in[ept_addr & 0x7F];
-    USB_SET_TXSTS(ept_info->eptn, USB_TX_STALL);
-  }
-  else
-  {
-    /* out endpoint */
-    ept_info = &udev->ept_out[ept_addr & 0x7F];
-    USB_SET_RXSTS(ept_info->eptn, USB_RX_STALL)
-  }
-
-  ept_info->stall = 1;
-}
-
-/**
-  * @brief  un-support device request
-  * @param  udev: usb device core handler type
-  * @retval none
-  */
-void usbd_ctrl_unsupport(usbd_core_type *udev)
-{
-  /* return stall status */
-  usbd_set_stall(udev, 0x00);
-  usbd_set_stall(udev, 0x80);
-}
-
-/**
-  * @brief  usb endpoint send data
-  * @param  udev: to the structure of usbd_core_type
-  * @param  ept_addr: endpoint number
-  * @param  buffer: send data buffer
-  * @param  len: send data length
-  * @retval none
-  */
-void usbd_ept_send(usbd_core_type *udev, uint8_t ept_addr, uint8_t *buffer, uint16_t len)
-{
-  /* get endpoint info struct and register */
-  usb_ept_info *ept_info = &udev->ept_in[ept_addr & 0x7F];
-  uint16_t trs_len = 0;
-  usbd_type *usbx = udev->usb_reg;
-
-  /* set send data buffer and length */
-  ept_info->trans_buf = buffer;
-  ept_info->total_len = len;
-  ept_info->trans_len = 0;
-
-  if(ept_info->total_len > ept_info->maxpacket)
-  {
-    trs_len = ept_info->maxpacket;
-    ept_info->total_len -= trs_len;
-  }
-  else
-  {
-    trs_len = len;
-    ept_info->total_len = 0;
-  }
-
-  ept_info->last_len = trs_len;
-
-  if(ept_info->is_double_buffer == 0)
-  {
-    /* write data to endpoint buffer */
-    usb_write_packet(ept_info->trans_buf, ept_info->tx_addr, trs_len);
-
-    /* set send data length */
-    USB_SET_TXLEN((ept_addr & 0x7F), trs_len);
-  }
-  else
-  {
-    if(usbx->ept_bit[ept_addr & 0x7F].txdts)
-    {
-      USB_SET_EPT_DOUBLE_BUF1_LEN((ept_addr & 0x7F), trs_len, DATA_TRANS_IN);
-      usb_write_packet(ept_info->trans_buf, ept_info->rx_addr, trs_len);
-    }
-    else
-    {
-      USB_SET_EPT_DOUBLE_BUF0_LEN((ept_addr & 0x7F), trs_len, DATA_TRANS_IN);
-      usb_write_packet(ept_info->trans_buf, ept_info->tx_addr, trs_len);
-    }
-    USB_FREE_DB_USER_BUFFER((ept_addr & 0x7F), DATA_TRANS_IN);
-  }
-
-  /* set tx status valid */
-  USB_SET_TXSTS((ept_addr & 0x7F), USB_TX_VALID);
-}
-
-/**
-  * @brief  usb endpoint receive data
-  * @param  udev: to the structure of usbd_core_type
-  * @param  ept_addr: endpoint number
-  * @param  buffer: receive data buffer
-  * @param  len: receive data length
-  * @retval none
-  */
-void usbd_ept_recv(usbd_core_type *udev, uint8_t ept_addr, uint8_t *buffer, uint16_t len)
-{
-  /* get endpoint info struct and register */
-  usb_ept_info *ept_info = &udev->ept_out[ept_addr & 0x7F];
-  uint32_t trs_len = 0;
-
-   /* set receive data buffer and length */
-  ept_info->trans_buf = buffer;
-  ept_info->total_len = len;
-  ept_info->trans_len = 0;
-
-  if(ept_info->total_len > ept_info->maxpacket)
-  {
-    trs_len = ept_info->maxpacket;
-    ept_info->total_len -= trs_len;
-  }
-  else
-  {
-    trs_len = len;
-    ept_info->total_len = 0;
-  }
-
-  /* set rx status valid */
-  USB_SET_RXSTS((ept_addr & 0x7F), USB_RX_VALID);
-}
-
-
-/**
-  * @brief  usb endpoint get receive data length
-  * @param  udev: to the structure of usbd_core_type
-  * @param  ept_addr: endpoint number
-  * @retval none
-  */
-uint32_t usbd_get_recv_len(usbd_core_type *udev, uint8_t ept_addr)
-{
-  usb_ept_info *ept = &udev->ept_out[ept_addr & 0x7F];
-  return ept->trans_len;
-}
-
-/**
-  * @brief  enable endpoint double buffer.
-  * @param  udev: to the structure of usbd_core_type
-  * @param  ept_addr: endpoint number
-  * @retval none
-  */
-void usbd_ept_dbuffer_enable(usbd_core_type *udev, uint8_t ept_addr)
-{
-  usb_ept_info *ept_info;
-  if((ept_addr & 0x80) == 0)
-  {
-    /* out endpoint info */
-    ept_info = &udev->ept_out[ept_addr & 0x7F];
-  }
-  else
-  {
-    /* in endpoint info */
-    ept_info = &udev->ept_in[ept_addr & 0x7F];
-  }
-  ept_info->is_double_buffer = TRUE;
-}
-
-/**
-  * @brief  usb auto define endpoint buffer
-  * @param  usb_ept_info: endpoint information
-  * @retval none
-  */
-void usbd_ept_buf_auto_define(usb_ept_info *ept_info)
-{
-  if(ept_info->is_double_buffer == 0)
-  {
-    if( ept_info->inout == DATA_TRANS_IN )
-    {
-      if(ept_info->tx_addr == 0)
-        ept_info->tx_addr = usb_buffer_malloc(ept_info->maxpacket);
-    }
-    else
-    {
-      if(ept_info->rx_addr == 0)
-        ept_info->rx_addr = usb_buffer_malloc(ept_info->maxpacket);
-    }
-  }
-  else
-  {
-    /* double buffer auto define */
-    if(ept_info->tx_addr == 0)
-      ept_info->tx_addr = usb_buffer_malloc(ept_info->maxpacket);
-    if(ept_info->rx_addr == 0)
-      ept_info->rx_addr = usb_buffer_malloc(ept_info->maxpacket);
-  }
-}
-
-
-
-/**
-  * @brief  usb custom define endpoint buffer
-  * @param  udev: to the structure of usbd_core_type
-  * @param  ept_addr: endpoint number
-  * @param  addr: usb fifo offset address
-  * @retval none
-  */
-void usbd_ept_buf_custom_define(usbd_core_type *udev, uint8_t ept_addr,
-                                uint32_t addr)
-{
-  usb_ept_info *ept_info;
-  if((ept_addr & 0x80) == 0)
-  {
-    /* out endpoint info */
-    ept_info = &udev->ept_out[ept_addr & 0x7F];
-  }
-  else
-  {
-    /* in endpoint info */
-    ept_info = &udev->ept_in[ept_addr & 0x7F];
-  }
-
-  if(ept_info->is_double_buffer == 0)
-  {
-    if( ept_info->inout == DATA_TRANS_IN )
-    {
-      ept_info->tx_addr = addr;
-    }
-    else
-    {
-      ept_info->rx_addr = addr;
-    }
-  }
-  else
-  {
-    /* double buffer malloc */
-    ept_info->tx_addr = addr & 0xFFFF;
-    ept_info->rx_addr = (addr >> 16) & 0xFFFF;
-  }
-}
-
-/**
-  * @brief  usb open endpoint
-  * @param  udev: to the structure of usbd_core_type
-  * @param  ept_addr: endpoint number
-  * @param  ept_type: endpoint type
-  * @param  maxpacket: endpoint support max buffer size
-  * @retval none
-  */
-void usbd_ept_open(usbd_core_type *udev, uint8_t ept_addr, uint8_t ept_type, uint16_t maxpacket)
-{
-  usbd_type *usbx = udev->usb_reg;
-  usb_ept_info *ept_info;
-
-  if((ept_addr & 0x80) == 0)
-  {
-    /* out endpoint info */
-    ept_info = &udev->ept_out[ept_addr & 0x7F];
-    ept_info->inout = DATA_TRANS_OUT;
-  }
-  else
-  {
-    /* in endpoint info */
-    ept_info = &udev->ept_in[ept_addr & 0x7F];
-    ept_info->inout = DATA_TRANS_IN;
-  }
-
-  /* set endpoint maxpacket and type */
-  ept_info->maxpacket = (maxpacket + 1) & 0xFFFE;
-  ept_info->trans_type = ept_type;
-
-#ifdef USB_EPT_AUTO_MALLOC_BUFFER
-  usbd_ept_buf_auto_define(ept_info);
-#endif
-  /* open endpoint */
-  usb_ept_open(usbx, ept_info);
-}
-
-/**
-  * @brief  usb close endpoint
-  * @param  udev: to the structure of usbd_core_type
-  * @param  ept_addr: endpoint number
-  * @retval none
-  */
-void usbd_ept_close(usbd_core_type *udev, uint8_t ept_addr)
-{
-  usb_ept_info *ept_info;
-  if(ept_addr & 0x80)
-  {
-    /* in endpoint */
-    ept_info = &udev->ept_in[ept_addr & 0x7F];
-  }
-  else
-  {
-    /* out endpoint */
-    ept_info = &udev->ept_out[ept_addr & 0x7F];
-  }
-
-  /* close endpoint */
-  usb_ept_close(udev->usb_reg, ept_info);
-}
-
-/**
-  * @brief  usb device connect to host
-  * @param  udev: to the structure of usbd_core_type
-  * @retval none
-  */
-void usbd_connect(usbd_core_type *udev)
-{
-  usb_connect(udev->usb_reg);
-}
-
-/**
-  * @brief  usb device disconnect to host
-  * @param  udev: to the structure of usbd_core_type
-  * @retval none
-  */
-void usbd_disconnect(usbd_core_type *udev)
-{
-  usb_disconnect(udev->usb_reg);
-}
-
-/**
-  * @brief  usb device set device address.
-  * @param  udev: to the structure of usbd_core_type
-  * @param  address: host assignment address
-  * @retval none
-  */
-void usbd_set_device_addr(usbd_core_type *udev, uint8_t address)
-{
-  usb_set_address(udev->usb_reg, address);
-}
-
-/**
-  * @brief  get usb connect state
-  * @param  udev: to the structure of usbd_core_type
-  * @retval usb connect state
-  */
-usbd_conn_state usbd_connect_state_get(usbd_core_type *udev)
-{
-  return udev->conn_state;
-}
-
-/**
-  * @brief  usb device remote wakeup
-  * @param  udev: to the structure of usbd_core_type
-  * @retval none
-  */
-void usbd_remote_wakeup(usbd_core_type *udev)
-{
-  /* set connect state */
-  udev->conn_state = udev->old_conn_state;
-
-  usb_exit_suspend(udev->usb_reg);
-
-  usb_remote_wkup_set(udev->usb_reg);
-
-  usb_delay_ms(10);
-
-  usb_remote_wkup_clear(udev->usb_reg);
-
-}
-
-/**
-  * @brief  usb device enter suspend mode
-  * @param  udev: to the structure of usbd_core_type
-  * @retval none
-  */
-void usbd_enter_suspend(usbd_core_type *udev)
-{
-  usb_enter_suspend(udev->usb_reg);
-}
-
-/**
-  * @brief  usb endpoint structure initialization
-  * @param  udev: to the structure of usbd_core_type
-  * @retval none
-  */
-void usbd_ept_defaut_init(usbd_core_type *udev)
-{
-  uint8_t i_index = 0;
-  /* init in endpoint info structure */
-  for(i_index = 0; i_index < USB_EPT_MAX_NUM; i_index ++)
-  {
-    udev->ept_in[i_index].eptn        = i_index;
-    udev->ept_in[i_index].ept_address = i_index;
-    udev->ept_in[i_index].inout         = DATA_TRANS_IN;
-    udev->ept_in[i_index].maxpacket   = 0;
-    udev->ept_in[i_index].trans_buf   = 0;
-    udev->ept_in[i_index].total_len   = 0;
-    udev->ept_in[i_index].tx_addr     = 0;
-    udev->ept_in[i_index].rx_addr     = 0;
-  }
-
-  /* init out endpoint info structure */
-  for(i_index = 0; i_index < USB_EPT_MAX_NUM; i_index ++)
-  {
-    udev->ept_out[i_index].eptn        = i_index;
-    udev->ept_out[i_index].ept_address = i_index;
-    udev->ept_out[i_index].inout         = DATA_TRANS_OUT;
-    udev->ept_out[i_index].maxpacket   = 0;
-    udev->ept_out[i_index].trans_buf   = 0;
-    udev->ept_out[i_index].total_len   = 0;
-    udev->ept_out[i_index].rx_addr     = 0;
-    udev->ept_out[i_index].tx_addr     = 0;
-  }
-  return;
-}
-
-/**
-  * @brief  initializes the usb core
-  * @param  udev: to the structure of usbd_core_type
-  * @param  usb_reg: usb register pointer (USB)
-  * @param  class_handler: usb class handler
-  * @param  desc_handler: device config handler
-  * @param  core_id: usb core id number
-  * @retval none
-  */
-void usbd_core_init(usbd_core_type *udev,
-                    usb_reg_type *usb_reg,
-                    usbd_class_handler *class_handler,
-                    usbd_desc_handler *desc_handler,
-                    uint8_t core_id)
-{
-  /* usb class handler */
-  udev->class_handler = class_handler;
-
-  /* usb description handler */
-  udev->desc_handler = desc_handler;
-
-  /* set usb register type */
-  udev->usb_reg = usb_reg;
-
-  /* set usb connect state to default */
-  udev->conn_state = USB_CONN_STATE_DEFAULT;
-
-  /* init in endpoint info structure */
-  usbd_ept_defaut_init(udev);
-
-#ifdef USB_BUFFER_SIZE_EX
-  /* usb buffer size extend 768-1280 byte */
-  usb_usbbufs_enable(usb_reg, TRUE);
-#endif
-
-  /*usb register config */
-  usb_dev_init(udev->usb_reg);
-}
-
-/**
-  * @}
-  */
-
-/**
-  * @}
-  */
-
-/**
-  * @}
-  */
+/**
+  **************************************************************************
+  * @file     usbd_core.c
+  * @version  v2.0.6
+  * @date     2021-12-31
+  * @brief    usb driver
+  **************************************************************************
+  *                       Copyright notice & Disclaimer
+  *
+  * The software Board Support Package (BSP) that is made available to 
+  * download from Artery official website is the copyrighted work of Artery. 
+  * Artery authorizes customers to use, copy, and distribute the BSP 
+  * software and its related documentation for the purpose of design and 
+  * development in conjunction with Artery microcontrollers. Use of the 
+  * software is governed by this copyright notice and the following disclaimer.
+  *
+  * THIS SOFTWARE IS PROVIDED ON "AS IS" BASIS WITHOUT WARRANTIES,
+  * GUARANTEES OR REPRESENTATIONS OF ANY KIND. ARTERY EXPRESSLY DISCLAIMS,
+  * TO THE FULLEST EXTENT PERMITTED BY LAW, ALL EXPRESS, IMPLIED OR
+  * STATUTORY OR OTHER WARRANTIES, GUARANTEES OR REPRESENTATIONS,
+  * INCLUDING BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
+  * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+  *
+  **************************************************************************
+  */
+#include "usbd_core.h"
+#include "usbd_sdr.h"
+
+/** @addtogroup AT32F403A_407_middlewares_usbd_drivers
+  * @{
+  */
+  
+/** @defgroup USBD_drivers_core
+  * @brief usb device drivers core
+  * @{
+  */  
+
+/** @defgroup USBD_core_private_functions
+  * @{
+  */
+  
+/**
+  * @brief  usb core in transfer complete handler
+  * @param  udev: to the structure of usbd_core_type
+  * @param  ept_addr: endpoint number
+  * @retval none
+  */
+void usbd_core_in_handler(usbd_core_type *udev, uint8_t ept_addr)
+{
+  /* get endpoint info*/
+  usb_ept_info *ept_info = &udev->ept_in[ept_addr & 0x7F];
+ 
+  if(ept_addr == 0)
+  {
+    /* endpoint 0 */
+    if(udev->ept0_sts == USB_EPT0_DATA_IN)
+    {
+      if(ept_info->ept0_slen > ept_info->maxpacket)
+      {
+        ept_info->ept0_slen -= ept_info->maxpacket;
+        usbd_ept_send(udev, 0, ept_info->trans_buf, 
+                      MIN(ept_info->ept0_slen, ept_info->maxpacket));
+      }
+      else if(ept_info->last_len == ept_info->maxpacket
+        && ept_info->ept0_slen <= udev->ept0_wlength)
+      {
+        ept_info->last_len = 0;
+        usbd_ept_send(udev, 0, 0, 0);
+        usbd_ept_recv(udev, ept_addr, 0, 0);
+      }
+      else
+      {
+        if(udev->class_handler->ept0_tx_handler != 0 && 
+            udev->conn_state == USB_CONN_STATE_CONFIGURED)
+          {
+            udev->class_handler->ept0_tx_handler(udev);
+          }
+          usbd_ctrl_recv_status(udev);
+      }
+    } 
+  }
+  else if(udev->class_handler->in_handler != 0 && 
+          udev->conn_state == USB_CONN_STATE_CONFIGURED)
+  {
+    /* other user define endpoint */
+    udev->class_handler->in_handler(udev, ept_addr);
+  }
+}
+
+/**
+  * @brief  usb core out transfer complete handler
+  * @param  udev: to the structure of usbd_core_type
+  * @param  ept_addr: endpoint number
+  * @retval none
+  */
+void usbd_core_out_handler(usbd_core_type *udev, uint8_t ept_addr)
+{
+   /* get endpoint info*/
+  usb_ept_info *ept_info = &udev->ept_out[ept_addr & 0x7F];
+  
+  if(ept_addr == 0)
+  {
+    /* endpoint 0 */
+    if(udev->ept0_sts == USB_EPT0_DATA_OUT)
+    {
+      if(ept_info->ept0_slen > ept_info->maxpacket)
+      {
+        ept_info->ept0_slen -= ept_info->maxpacket;
+        usbd_ept_recv(udev, ept_addr, ept_info->trans_buf, 
+                      MIN(ept_info->ept0_slen, ept_info->maxpacket));
+      }
+      else
+      {
+          if(udev->class_handler->ept0_rx_handler != 0 && 
+             udev->conn_state == USB_CONN_STATE_CONFIGURED)
+          {
+            udev->class_handler->ept0_rx_handler(udev);
+          }
+          usbd_ctrl_send_status(udev);
+      }
+    }
+  }
+  else if(udev->class_handler->out_handler != 0 && 
+          udev->conn_state == USB_CONN_STATE_CONFIGURED)
+  {
+    /* other user define endpoint */
+    udev->class_handler->out_handler(udev, ept_addr);
+  }
+}
+
+/**
+  * @brief  usb core setup transfer complete handler
+  * @param  udev: to the structure of usbd_core_type
+  * @param  ept_addr: endpoint number
+  * @retval none
+  */
+void usbd_core_setup_handler(usbd_core_type *udev, uint8_t ept_num)
+{
+  /* setup parse */
+  usbd_setup_request_parse(&udev->setup, udev->setup_buffer);
+  
+  /* set ept0 status */
+  udev->ept0_sts = USB_EPT0_SETUP;
+  udev->ept0_wlength = udev->setup.wLength;
+  
+  switch(udev->setup.bmRequestType & USB_REQ_RECIPIENT_MASK)
+  {
+    case USB_REQ_RECIPIENT_DEVICE:
+      /* recipient device request */
+      usbd_device_request(udev);
+      break;
+    case USB_REQ_RECIPIENT_INTERFACE:
+      /* recipient interface request */
+      usbd_interface_request(udev);
+      break;
+    case USB_REQ_RECIPIENT_ENDPOINT:
+      /* recipient endpoint request */
+      usbd_endpoint_request(udev);
+      break;
+    default:
+      break;
+  }
+}
+
+/**
+  * @brief  usb control endpoint send data
+  * @param  udev: to the structure of usbd_core_type
+  * @param  ept_addr: endpoint number
+  * @param  buffer: send data buffer
+  * @param  len: send data length
+  * @retval none
+  */
+void usbd_ctrl_send(usbd_core_type *udev, uint8_t *buffer, uint16_t len)
+{
+  usb_ept_info *ept_info = &udev->ept_in[0];
+  
+  ept_info->ept0_slen = len;
+  udev->ept0_sts = USB_EPT0_DATA_IN;
+  
+  usbd_ept_send(udev, 0, buffer, len);
+}
+
+/**
+  * @brief  usb control endpoint receive data
+  * @param  udev: to the structure of usbd_core_type
+  * @param  ept_addr: endpoint number
+  * @param  buffer: recv data buffer
+  * @param  len: recv data length
+  * @retval none
+  */
+void usbd_ctrl_recv(usbd_core_type *udev, uint8_t *buffer, uint16_t len)
+{
+  usb_ept_info *ept_info = &udev->ept_out[0];
+
+  ept_info->ept0_slen = len;
+  udev->ept0_sts = USB_EPT0_DATA_OUT;
+  
+  usbd_ept_recv(udev, 0, buffer, len);
+}
+
+/**
+  * @brief  usb control endpoint send in status
+  * @param  udev: to the structure of usbd_core_type
+  * @retval none
+  */
+void usbd_ctrl_send_status(usbd_core_type *udev)
+{
+  udev->ept0_sts = USB_EPT0_STATUS_IN;
+  
+  usbd_ept_send(udev, 0, NULL, 0);
+}
+
+/**
+  * @brief  usb control endpoint send out status
+  * @param  udev: usb device core handler type
+  * @retval none
+  */
+void usbd_ctrl_recv_status(usbd_core_type *udev)
+{
+  udev->ept0_sts = USB_EPT0_STATUS_OUT;
+  
+  usbd_ept_recv(udev, 0, NULL, 0);
+}
+
+/**
+  * @brief  clear endpoint stall
+  * @param  udev: to the structure of usbd_core_type
+  * @param  ept_addr: endpoint number
+  * @retval none
+  */
+void usbd_clear_stall(usbd_core_type *udev, uint8_t ept_addr)
+{
+  usb_ept_info *ept_info;
+  
+  if(ept_addr & 0x80) 
+  {
+    /* in endpoint */
+    ept_info = &udev->ept_in[ept_addr & 0x7F];
+    USB_SET_TXSTS(ept_info->eptn, USB_TX_VALID);
+  }
+  else
+  {
+    /* out endpoint */
+    ept_info = &udev->ept_out[ept_addr & 0x7F];
+    USB_SET_RXSTS(ept_info->eptn, USB_RX_VALID);
+  }
+  ept_info->stall = 0;
+}
+
+/**
+  * @brief  usb set endpoint to stall.
+  * @param  udev: to the structure of usbd_core_type
+  * @param  ept_addr: endpoint number
+  * @retval none
+  */
+void usbd_set_stall(usbd_core_type *udev, uint8_t ept_addr)
+{
+  usb_ept_info *ept_info;
+  
+  if(ept_addr & 0x80)
+  {
+    /* in endpoint */
+    ept_info = &udev->ept_in[ept_addr & 0x7F];
+    USB_SET_TXSTS(ept_info->eptn, USB_TX_STALL);
+  }
+  else
+  {
+    /* out endpoint */
+    ept_info = &udev->ept_out[ept_addr & 0x7F];
+    USB_SET_RXSTS(ept_info->eptn, USB_RX_STALL)
+  }
+  
+  ept_info->stall = 1;
+}
+
+/**
+  * @brief  un-support device request
+  * @param  udev: usb device core handler type
+  * @retval none
+  */
+void usbd_ctrl_unsupport(usbd_core_type *udev)
+{
+  /* return stall status */
+  usbd_set_stall(udev, 0x00);
+  usbd_set_stall(udev, 0x80);
+}
+
+/**
+  * @brief  usb endpoint send data
+  * @param  udev: to the structure of usbd_core_type
+  * @param  ept_addr: endpoint number
+  * @param  buffer: send data buffer
+  * @param  len: send data length
+  * @retval none
+  */
+void usbd_ept_send(usbd_core_type *udev, uint8_t ept_addr, uint8_t *buffer, uint16_t len)
+{
+  /* get endpoint info struct and register */
+  usb_ept_info *ept_info = &udev->ept_in[ept_addr & 0x7F];
+  uint16_t trs_len = 0;
+  usbd_type *usbx = udev->usb_reg;
+  
+  /* set send data buffer and length */
+  ept_info->trans_buf = buffer;
+  ept_info->total_len = len;
+  ept_info->trans_len = 0;
+  
+  if(ept_info->total_len > ept_info->maxpacket)
+  {
+    trs_len = ept_info->maxpacket;
+    ept_info->total_len -= trs_len;
+  }
+  else
+  {
+    trs_len = len;
+    ept_info->total_len = 0;
+  }
+  
+  ept_info->last_len = trs_len;
+  
+  if(ept_info->is_double_buffer == 0)
+  {
+    /* write data to endpoint buffer */
+    usb_write_packet(ept_info->trans_buf, ept_info->tx_addr, trs_len);
+   
+    /* set send data length */
+    USB_SET_TXLEN((ept_addr & 0x7F), trs_len);
+  }
+  else
+  {
+    if(usbx->ept_bit[ept_addr & 0x7F].txdts)
+    {
+      USB_SET_EPT_DOUBLE_BUF1_LEN((ept_addr & 0x7F), trs_len, DATA_TRANS_IN);
+      usb_write_packet(ept_info->trans_buf, ept_info->rx_addr, trs_len);
+    }
+    else
+    {
+      USB_SET_EPT_DOUBLE_BUF0_LEN((ept_addr & 0x7F), trs_len, DATA_TRANS_IN);
+      usb_write_packet(ept_info->trans_buf, ept_info->tx_addr, trs_len);
+    }
+    USB_FREE_DB_USER_BUFFER((ept_addr & 0x7F), DATA_TRANS_IN);
+  }
+  
+  /* set tx status valid */
+  USB_SET_TXSTS((ept_addr & 0x7F), USB_TX_VALID);
+}
+
+/**
+  * @brief  usb endpoint receive data
+  * @param  udev: to the structure of usbd_core_type
+  * @param  ept_addr: endpoint number
+  * @param  buffer: receive data buffer
+  * @param  len: receive data length
+  * @retval none
+  */
+void usbd_ept_recv(usbd_core_type *udev, uint8_t ept_addr, uint8_t *buffer, uint16_t len)
+{
+  /* get endpoint info struct and register */
+  usb_ept_info *ept_info = &udev->ept_out[ept_addr & 0x7F];
+  uint32_t trs_len = 0;
+  
+   /* set receive data buffer and length */
+  ept_info->trans_buf = buffer;
+  ept_info->total_len = len;
+  ept_info->trans_len = 0;
+  
+  if(ept_info->total_len > ept_info->maxpacket)
+  {
+    trs_len = ept_info->maxpacket;
+    ept_info->total_len -= trs_len;
+  }
+  else
+  {
+    trs_len = len;
+    ept_info->total_len = 0;
+  }
+  
+  /* set rx status valid */
+  USB_SET_RXSTS((ept_addr & 0x7F), USB_RX_VALID);
+}
+
+
+/**
+  * @brief  usb endpoint get receive data length
+  * @param  udev: to the structure of usbd_core_type
+  * @param  ept_addr: endpoint number
+  * @retval none
+  */
+uint32_t usbd_get_recv_len(usbd_core_type *udev, uint8_t ept_addr)
+{
+  usb_ept_info *ept = &udev->ept_out[ept_addr & 0x7F];
+  return ept->trans_len;
+}
+
+/**
+  * @brief  enable endpoint double buffer.
+  * @param  udev: to the structure of usbd_core_type
+  * @param  ept_addr: endpoint number
+  * @retval none
+  */
+void usbd_ept_dbuffer_enable(usbd_core_type *udev, uint8_t ept_addr)
+{
+  usb_ept_info *ept_info;
+  if((ept_addr & 0x80) == 0)
+  {
+    /* out endpoint info */
+    ept_info = &udev->ept_out[ept_addr & 0x7F];
+  }
+  else
+  {
+    /* in endpoint info */
+    ept_info = &udev->ept_in[ept_addr & 0x7F];
+  }
+  ept_info->is_double_buffer = TRUE;
+}
+
+/**
+  * @brief  usb auto define endpoint buffer
+  * @param  usb_ept_info: endpoint information
+  * @retval none                            
+  */
+void usbd_ept_buf_auto_define(usb_ept_info *ept_info)
+{
+  if(ept_info->is_double_buffer == 0)
+  {
+    if( ept_info->inout == DATA_TRANS_IN )
+    {
+      if(ept_info->tx_addr == 0)
+        ept_info->tx_addr = usb_buffer_malloc(ept_info->maxpacket);
+    }
+    else
+    {
+      if(ept_info->rx_addr == 0)
+        ept_info->rx_addr = usb_buffer_malloc(ept_info->maxpacket);
+    }
+  }
+  else
+  {
+    /* double buffer auto define */
+    if(ept_info->tx_addr == 0)
+      ept_info->tx_addr = usb_buffer_malloc(ept_info->maxpacket);
+    if(ept_info->rx_addr == 0)
+      ept_info->rx_addr = usb_buffer_malloc(ept_info->maxpacket);
+  }
+}
+
+
+
+/**
+  * @brief  usb custom define endpoint buffer
+  * @param  udev: to the structure of usbd_core_type
+  * @param  ept_addr: endpoint number
+  * @param  addr: usb fifo offset address
+  * @retval none                            
+  */
+void usbd_ept_buf_custom_define(usbd_core_type *udev, uint8_t ept_addr, 
+                                uint32_t addr)
+{
+  usb_ept_info *ept_info;
+  if((ept_addr & 0x80) == 0)
+  {
+    /* out endpoint info */
+    ept_info = &udev->ept_out[ept_addr & 0x7F];
+  }
+  else
+  {
+    /* in endpoint info */
+    ept_info = &udev->ept_in[ept_addr & 0x7F];
+  }
+  
+  if(ept_info->is_double_buffer == 0)
+  {
+    if( ept_info->inout == DATA_TRANS_IN )
+    {
+      ept_info->tx_addr = addr;
+    }
+    else
+    {
+      ept_info->rx_addr = addr;
+    }
+  }
+  else
+  {
+    /* double buffer malloc */
+    ept_info->tx_addr = addr & 0xFFFF;
+    ept_info->rx_addr = (addr >> 16) & 0xFFFF;
+  }
+}
+
+/**
+  * @brief  usb open endpoint
+  * @param  udev: to the structure of usbd_core_type
+  * @param  ept_addr: endpoint number
+  * @param  ept_type: endpoint type
+  * @param  maxpacket: endpoint support max buffer size
+  * @retval none
+  */
+void usbd_ept_open(usbd_core_type *udev, uint8_t ept_addr, uint8_t ept_type, uint16_t maxpacket)
+{
+  usbd_type *usbx = udev->usb_reg;
+  usb_ept_info *ept_info;
+  
+  if((ept_addr & 0x80) == 0)
+  {
+    /* out endpoint info */
+    ept_info = &udev->ept_out[ept_addr & 0x7F];
+    ept_info->inout = DATA_TRANS_OUT;
+  }
+  else
+  {
+    /* in endpoint info */
+    ept_info = &udev->ept_in[ept_addr & 0x7F];
+    ept_info->inout = DATA_TRANS_IN;
+  }
+  
+  /* set endpoint maxpacket and type */
+  ept_info->maxpacket = (maxpacket + 1) & 0xFFFE;
+  ept_info->trans_type = ept_type;
+  
+#ifdef USB_EPT_AUTO_MALLOC_BUFFER
+  usbd_ept_buf_auto_define(ept_info);
+#endif
+  /* open endpoint */
+  usb_ept_open(usbx, ept_info);
+}
+
+/**
+  * @brief  usb close endpoint
+  * @param  udev: to the structure of usbd_core_type
+  * @param  ept_addr: endpoint number
+  * @retval none
+  */
+void usbd_ept_close(usbd_core_type *udev, uint8_t ept_addr)
+{
+  usb_ept_info *ept_info; 
+  if(ept_addr & 0x80)
+  {
+    /* in endpoint */
+    ept_info = &udev->ept_in[ept_addr & 0x7F];
+  }
+  else
+  {
+    /* out endpoint */
+    ept_info = &udev->ept_out[ept_addr & 0x7F];
+  }
+  
+  /* close endpoint */
+  usb_ept_close(udev->usb_reg, ept_info);
+}
+
+/**
+  * @brief  usb device connect to host
+  * @param  udev: to the structure of usbd_core_type
+  * @retval none
+  */
+void usbd_connect(usbd_core_type *udev)
+{
+  usb_connect(udev->usb_reg);
+}
+
+/**
+  * @brief  usb device disconnect to host
+  * @param  udev: to the structure of usbd_core_type
+  * @retval none
+  */
+void usbd_disconnect(usbd_core_type *udev)
+{
+  usb_disconnect(udev->usb_reg);
+}
+
+/**
+  * @brief  usb device set device address.
+  * @param  udev: to the structure of usbd_core_type
+  * @param  address: host assignment address
+  * @retval none
+  */
+void usbd_set_device_addr(usbd_core_type *udev, uint8_t address)
+{
+  usb_set_address(udev->usb_reg, address);
+}
+
+/**
+  * @brief  get usb connect state
+  * @param  udev: to the structure of usbd_core_type
+  * @retval usb connect state
+  */
+usbd_conn_state usbd_connect_state_get(usbd_core_type *udev)
+{
+  return udev->conn_state;
+}
+
+/**
+  * @brief  usb device remote wakeup
+  * @param  udev: to the structure of usbd_core_type
+  * @retval none
+  */
+void usbd_remote_wakeup(usbd_core_type *udev)
+{
+  /* set connect state */
+  udev->conn_state = udev->old_conn_state;
+  
+  usb_exit_suspend(udev->usb_reg);
+  
+  usb_remote_wkup_set(udev->usb_reg);
+  
+  usb_delay_ms(10);
+  
+  usb_remote_wkup_clear(udev->usb_reg);
+  
+}
+
+/**
+  * @brief  usb device enter suspend mode
+  * @param  udev: to the structure of usbd_core_type
+  * @retval none
+  */
+void usbd_enter_suspend(usbd_core_type *udev)
+{
+  usb_enter_suspend(udev->usb_reg);
+}
+
+/**
+  * @brief  usb endpoint structure initialization
+  * @param  udev: to the structure of usbd_core_type
+  * @retval none
+  */
+void usbd_ept_defaut_init(usbd_core_type *udev)
+{
+  uint8_t i_index = 0;
+  /* init in endpoint info structure */
+  for(i_index = 0; i_index < USB_EPT_MAX_NUM; i_index ++)
+  {
+    udev->ept_in[i_index].eptn        = i_index;
+    udev->ept_in[i_index].ept_address = i_index;
+    udev->ept_in[i_index].inout         = DATA_TRANS_IN;
+    udev->ept_in[i_index].maxpacket   = 0;
+    udev->ept_in[i_index].trans_buf   = 0;
+    udev->ept_in[i_index].total_len   = 0;
+    udev->ept_in[i_index].tx_addr     = 0;     
+  }
+  
+  /* init out endpoint info structure */
+  for(i_index = 0; i_index < USB_EPT_MAX_NUM; i_index ++)
+  {
+    udev->ept_out[i_index].eptn        = i_index;
+    udev->ept_out[i_index].ept_address = i_index;
+    udev->ept_out[i_index].inout         = DATA_TRANS_OUT;
+    udev->ept_out[i_index].maxpacket   = 0;
+    udev->ept_out[i_index].trans_buf   = 0;
+    udev->ept_out[i_index].total_len   = 0;
+    udev->ept_out[i_index].rx_addr     = 0; 
+  }
+  return;
+}
+
+/**
+  * @brief  initializes the usb core 
+  * @param  udev: to the structure of usbd_core_type
+  * @param  usb_reg: usb register pointer (USB)
+  * @param  class_handler: usb class handler
+  * @param  desc_handler: device config handler
+  * @param  core_id: usb core id number
+  * @retval none
+  */
+void usbd_core_init(usbd_core_type *udev,
+                    usb_reg_type *usb_reg,
+                    usbd_class_handler *class_handler, 
+                    usbd_desc_handler *desc_handler,
+                    uint8_t core_id)
+{
+  /* usb class handler */
+  udev->class_handler = class_handler;
+  
+  /* usb description handler */
+  udev->desc_handler = desc_handler;
+  
+  /* set usb register type */
+  udev->usb_reg = usb_reg;
+  
+  /* set usb connect state to default */
+  udev->conn_state = USB_CONN_STATE_DEFAULT;
+  
+  /* init in endpoint info structure */
+  usbd_ept_defaut_init(udev);
+  
+#ifdef USB_BUFFER_SIZE_EX
+  /* usb buffer size extend 768-1280 byte */
+  usb_usbbufs_enable(usb_reg, TRUE);
+#endif
+  
+  /*usb register config */
+  usb_dev_init(udev->usb_reg);
+}
+
+/**
+  * @}
+  */ 
+
+/**
+  * @}
+  */
+
+/**
+  * @}
+  */ 

+ 341 - 336
libs/thirdparty/rndis/usbd_drivers/src/usbd_int.c → libs/artery/usb/src/usbd_int.c

@@ -1,336 +1,341 @@
-/**
-  **************************************************************************
-  * @file     usbd_int.c
-  * @version  v2.1.2
-  * @date     2022-08-16
-  * @brief    usb interrupt request
-  **************************************************************************
-  *                       Copyright notice & Disclaimer
-  *
-  * The software Board Support Package (BSP) that is made available to
-  * download from Artery official website is the copyrighted work of Artery.
-  * Artery authorizes customers to use, copy, and distribute the BSP
-  * software and its related documentation for the purpose of design and
-  * development in conjunction with Artery microcontrollers. Use of the
-  * software is governed by this copyright notice and the following disclaimer.
-  *
-  * THIS SOFTWARE IS PROVIDED ON "AS IS" BASIS WITHOUT WARRANTIES,
-  * GUARANTEES OR REPRESENTATIONS OF ANY KIND. ARTERY EXPRESSLY DISCLAIMS,
-  * TO THE FULLEST EXTENT PERMITTED BY LAW, ALL EXPRESS, IMPLIED OR
-  * STATUTORY OR OTHER WARRANTIES, GUARANTEES OR REPRESENTATIONS,
-  * INCLUDING BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
-  * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
-  *
-  **************************************************************************
-  */
-#include "usbd_int.h"
-#include "common_config.h"
-
-/** @addtogroup AT32F403A_407_middlewares_usbd_drivers
-  * @{
-  */
-
-/** @defgroup USBD_drivers_interrupt
-  * @brief usb device interrupt
-  * @{
-  */
-
-/** @defgroup USBD_int_private_functions
-  * @{
-  */
-
-/**
-  * @brief  usb device interrput request handler.
-  * @param  udev: to the structure of usbd_core_type
-  * @retval none
-  */
-void usbd_irq_handler(usbd_core_type *udev)
-{
-  usbd_type *usbx = udev->usb_reg;
-  uint32_t sts_val = usbx->intsts;
-  uint32_t sts_ien = usbx->ctrl;
-
-  if(sts_val & USB_TC_FLAG)
-  {
-    /* endpoint tc interrupt handler */
-    usbd_ept_loop_handler(udev);
-  }
-
-  if(sts_val & USB_RST_FLAG)
-  {
-    /* clear reset flag */
-    usb_flag_clear(usbx, USB_RST_FLAG);
-
-    UDBG printf("reset handler\r\n");
-    
-    /* reset interrupt handler */
-    usbd_reset_handler(udev);
-  }
-
-  if((sts_val & USB_SOF_FLAG) &&
-    (sts_ien & USB_SOF_INT))
-  {
-    /* clear sof flag */
-    usb_flag_clear(usbx, USB_SOF_FLAG);
-
-    /* sof interrupt handler */
-    usbd_sof_handler(udev);
-  }
-
-  if((sts_val & USB_LSOF_FLAG) &&
-    (sts_ien & USB_LSOF_INT))
-  {
-    /* clear lsof flag */
-    usb_flag_clear(usbx, USB_LSOF_FLAG);
-  }
-
-  if((sts_val & USB_SP_FLAG) &&
-    (sts_ien & USB_SP_INT))
-  {
-    /* clear suspend flag */
-    usb_flag_clear(usbx, USB_SP_FLAG);
-
-    /* usb suspend interrupt handler */
-    usbd_suspend_handler(udev);
-  }
-
-  if((sts_val & USB_WK_FLAG) &&
-    (sts_ien & USB_WK_INT))
-  {
-    /* usb wakeup interrupt handler */
-    usbd_wakeup_handler(udev);
-
-    /* clear wakeup flag */
-    usb_flag_clear(usbx, USB_WK_FLAG);
-  }
-}
-
-/**
-  * @brief  usb device endpoint request handler.
-  * @param  udev: to the structure of usbd_core_type
-  * @param  ept_num: endpoint number
-  * @retval none
-  */
-void usbd_eptn_handler(usbd_core_type *udev, usb_ept_number_type ept_num)
-{
-  usbd_type *usbx = udev->usb_reg;
-  usb_ept_info *ept_info;
-  uint32_t ept_val = usbx->ept[ept_num];
-  uint16_t length;
-
-  /* in interrupt request  */
-  if(ept_val & USB_TXTC)
-  {
-    /* get endpoint register and in transfer info struct */
-    ept_info = &udev->ept_in[ept_num];
-
-    /* clear endpoint tc flag */
-    USB_CLEAR_TXTC(ept_num);
-
-    /* get endpoint tx length */
-    ept_info->trans_len = USB_GET_TX_LEN(ept_num);
-
-    /* offset the trans buffer */
-    ept_info->trans_buf += ept_info->trans_len;
-
-    if(ept_info->total_len == 0 || ept_num == USB_EPT0)
-    {
-      /* in transfer complete */
-      usbd_core_in_handler(udev, ept_num);
-    }
-    else
-    {
-      /* endpoint continue send data */
-      usbd_ept_send(udev, ept_num, ept_info->trans_buf, ept_info->total_len);
-    }
-    /* set the host assignment address */
-    if(udev->conn_state == USB_CONN_STATE_ADDRESSED && udev->device_addr > 0)
-    {
-      usb_set_address(udev->usb_reg, udev->device_addr);
-      udev->device_addr = 0;
-    }
-  }
-  else
-  {
-    /* setup and out interrupt request */
-
-    /* get endpoint register and out transfer info struct */
-    ept_info = &udev->ept_out[ept_num];
-
-    if((ept_val & USB_SETUPTC) != 0)
-    {
-      /* endpoint setup interrupt request */
-
-      /* get endpoint received data length */
-      ept_info->trans_len = USB_GET_RX_LEN(ept_num);
-
-      /* read endpoint received data */
-      usb_read_packet(udev->setup_buffer, ept_info->rx_addr, ept_info->trans_len);
-
-      /* clear endpoint rx tc flag */
-      USB_CLEAR_RXTC(USB_EPT0);
-
-      /* endpoint setup complete handler */
-      usbd_core_setup_handler(udev, ept_num);
-    }
-    else if(ept_val & USB_RXTC )
-    {
-      /* endpoint out interrupt request */
-      USB_CLEAR_RXTC(ept_num);
-
-      if(ept_info->is_double_buffer == 0)
-      {
-        /* get endpoint received data length */
-        length = USB_GET_RX_LEN(ept_num);
-
-        /* read endpoint received data */
-        usb_read_packet(ept_info->trans_buf, ept_info->rx_addr, length);
-      }
-      else
-      {
-        if( ept_val & USB_RXDTS)
-        {
-          length = USB_DBUF0_GET_LEN(ept_num);
-          usb_read_packet(ept_info->trans_buf, ept_info->tx_addr, length);
-        }
-        else
-        {
-          length = USB_DBUF1_GET_LEN(ept_num);
-          usb_read_packet(ept_info->trans_buf, ept_info->rx_addr, length);
-        }
-        USB_FREE_DB_USER_BUFFER(ept_num, DATA_TRANS_OUT);
-      }
-
-      /* set received data length */
-      ept_info->trans_len += length;
-      ept_info->trans_buf += length;
-
-      if(ept_info->total_len == 0 || length < ept_info->maxpacket || ept_num == USB_EPT0)
-      {
-        /* out transfer complete */
-        usbd_core_out_handler(udev, ept_num);
-      }
-      else
-      {
-        /* endpoint continue receive data  */
-        usbd_ept_recv(udev, ept_num, ept_info->trans_buf, ept_info->total_len);
-      }
-    }
-  }
-}
-
-/**
-  * @brief  usb device endpoint loop handler.
-  * @param  udev: to the structure of usbd_core_type
-  * @retval none
-  */
-void usbd_ept_loop_handler(usbd_core_type *udev)
-{
-  usbd_type *usbx = udev->usb_reg;
-  usb_ept_number_type ept_num = USB_EPT0;
-  uint32_t sts_val;
-
-  while((sts_val = usbx->intsts) & USB_TC_FLAG)
-  {
-    /* get the interrupt endpoint number */
-    ept_num = (usb_ept_number_type)(sts_val & USB_EPT_NUM_FLAG);
-
-    usbd_eptn_handler(udev, ept_num);
-  }
-}
-
-/**
-  * @brief  usb device reset interrupt request handler.
-  * @param  udev: to the structure of usbd_core_type
-  * @retval none
-  */
-void usbd_reset_handler(usbd_core_type *udev)
-{
-  /* free usb buffer */
-  usb_buffer_free();
-
-  usbd_ept_defaut_init(udev);
-#ifndef USB_EPT_AUTO_MALLOC_BUFFER
-  usbd_ept_buf_custom_define(udev, 0x80, EPT0_TX_ADDR);
-  usbd_ept_buf_custom_define(udev, 0x00, EPT0_RX_ADDR);
-#endif
-
-  /* open endpoint 0 out */
-  usbd_ept_open(udev, 0x00, EPT_CONTROL_TYPE, 0x40);
-
-  /* open endpoint 0 in */
-  usbd_ept_open(udev, 0x80, EPT_CONTROL_TYPE, 0x40);
-
-  /* set device address to 0 */
-  usb_set_address(udev->usb_reg, 0);
-
-  /* usb connect state set to default */
-  udev->conn_state = USB_CONN_STATE_DEFAULT;
-
-  /* user define reset event */
-  if(udev->class_handler->event_handler)
-    udev->class_handler->event_handler(udev, USBD_RESET_EVENT);
-}
-
-/**
-  * @brief  usb device sof interrupt request handler.
-  * @param  udev: to the structure of usbd_core_type
-  * @retval none
-  */
-void usbd_sof_handler(usbd_core_type *udev)
-{
-  /* user sof handler in class define*/
-  if(udev->class_handler->sof_handler)
-    udev->class_handler->sof_handler(udev);
-}
-
-/**
-  * @brief  usb device suspend interrupt request handler.
-  * @param  udev: to the structure of usbd_core_type
-  * @retval none
-  */
-void usbd_suspend_handler(usbd_core_type *udev)
-{
-  /* save connect state */
-  udev->old_conn_state = udev->conn_state;
-
-  /* set current state to suspend */
-  udev->conn_state = USB_CONN_STATE_SUSPENDED;
-
-  /* enter suspend mode */
-  usbd_enter_suspend(udev);
-
-  /* user define suspend event */
-  if(udev->class_handler->event_handler)
-    udev->class_handler->event_handler(udev, USBD_SUSPEND_EVENT);
-}
-
-/**
-  * @brief  usb device wakup interrupt request handler.
-  * @param  udev: to the structure of usbd_core_type
-  * @retval none
-  */
-void usbd_wakeup_handler(usbd_core_type *udev)
-{
-  /* exit suspend mode */
-  usb_exit_suspend(udev->usb_reg);
-
-  /* restore connect state */
-  udev->conn_state = udev->old_conn_state;
-
-  /* user define wakeup event */
-  if(udev->class_handler->event_handler)
-    udev->class_handler->event_handler(udev, USBD_WAKEUP_EVENT);
-}
-
-/**
-  * @}
-  */
-
-/**
-  * @}
-  */
-
-/**
-  * @}
-  */
+/**
+  **************************************************************************
+  * @file     usbd_int.c
+  * @version  v2.0.6
+  * @date     2021-12-31
+  * @brief    usb interrupt request
+  **************************************************************************
+  *                       Copyright notice & Disclaimer
+  *
+  * The software Board Support Package (BSP) that is made available to 
+  * download from Artery official website is the copyrighted work of Artery. 
+  * Artery authorizes customers to use, copy, and distribute the BSP 
+  * software and its related documentation for the purpose of design and 
+  * development in conjunction with Artery microcontrollers. Use of the 
+  * software is governed by this copyright notice and the following disclaimer.
+  *
+  * THIS SOFTWARE IS PROVIDED ON "AS IS" BASIS WITHOUT WARRANTIES,
+  * GUARANTEES OR REPRESENTATIONS OF ANY KIND. ARTERY EXPRESSLY DISCLAIMS,
+  * TO THE FULLEST EXTENT PERMITTED BY LAW, ALL EXPRESS, IMPLIED OR
+  * STATUTORY OR OTHER WARRANTIES, GUARANTEES OR REPRESENTATIONS,
+  * INCLUDING BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
+  * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+  *
+  **************************************************************************
+  */
+#include "usbd_int.h"
+#include "common_config.h"
+#include "usbd_conf.h"
+#ifdef PRINTF_STDLIB
+#include <stdio.h>
+#endif
+#ifdef PRINTF_CUSTOM
+#include "tinystdio.h"
+#endif
+
+/** @addtogroup AT32F403A_407_middlewares_usbd_drivers
+  * @{
+  */
+  
+/** @defgroup USBD_drivers_interrupt
+  * @brief usb device interrupt
+  * @{
+  */  
+
+/** @defgroup USBD_int_private_functions
+  * @{
+  */
+
+/**
+  * @brief  usb device interrput request handler.
+  * @param  udev: to the structure of usbd_core_type
+  * @retval none
+  */
+void usbd_irq_handler(usbd_core_type *udev)
+{
+  usbd_type *usbx = udev->usb_reg;
+  uint32_t sts_val = usbx->intsts;
+  uint32_t sts_ien = usbx->ctrl;
+  
+  if(sts_val & USB_TC_FLAG)
+  {
+    /* endpoint tc interrupt handler */
+    usbd_ept_loop_handler(udev);
+  }
+  
+  if(sts_val & USB_RST_FLAG)
+  {
+    /* clear reset flag */
+    usb_flag_clear(usbx, USB_RST_FLAG);
+    
+    /* reset interrupt handler */
+    usbd_reset_handler(udev);
+  }
+  
+  if((sts_val & USB_SOF_FLAG) && 
+    (sts_ien & USB_SOF_INT))
+  {
+    /* clear sof flag */
+    usb_flag_clear(usbx, USB_SOF_FLAG);
+    
+    /* sof interrupt handler */
+    usbd_sof_handler(udev);
+  }
+  
+  if((sts_val & USB_LSOF_FLAG) && 
+    (sts_ien & USB_LSOF_INT))
+  {
+    /* clear lsof flag */
+    usb_flag_clear(usbx, USB_LSOF_FLAG);
+  }
+  
+  if((sts_val & USB_SP_FLAG) && 
+    (sts_ien & USB_SP_INT))
+  {
+    /* clear suspend flag */
+    usb_flag_clear(usbx, USB_SP_FLAG);
+    
+    /* usb suspend interrupt handler */
+    usbd_suspend_handler(udev);
+  }
+  
+  if((sts_val & USB_WK_FLAG) && 
+    (sts_ien & USB_WK_INT))
+  {
+    /* usb wakeup interrupt handler */
+    usbd_wakeup_handler(udev);
+    
+    /* clear wakeup flag */
+    usb_flag_clear(usbx, USB_WK_FLAG);
+  }
+}
+
+
+/**
+  * @brief  usb device endpoint request handler.
+  * @param  udev: to the structure of usbd_core_type
+  * @param  ept_num: endpoint number
+  * @retval none
+  */
+void usbd_eptn_handler(usbd_core_type *udev, usb_ept_number_type ept_num)
+{
+  usbd_type *usbx = udev->usb_reg;
+  usb_ept_info *ept_info;
+  uint32_t ept_val = usbx->ept[ept_num];
+  uint16_t length;
+      
+  /* in interrupt request  */
+  if(ept_val & USB_TXTC)
+  {
+    /* get endpoint register and in transfer info struct */
+    ept_info = &udev->ept_in[ept_num];
+    
+    /* clear endpoint tc flag */
+    USB_CLEAR_TXTC(ept_num);
+    
+    /* get endpoint tx length */
+    ept_info->trans_len = USB_GET_TX_LEN(ept_num);
+    
+    /* offset the trans buffer */
+    ept_info->trans_buf += ept_info->trans_len;
+    
+    if(ept_info->total_len == 0 || ept_num == USB_EPT0)
+    {
+      /* in transfer complete */
+      usbd_core_in_handler(udev, ept_num);
+    }
+    else
+    {
+      /* endpoint continue send data */
+      usbd_ept_send(udev, ept_num, ept_info->trans_buf, ept_info->total_len);
+    }
+    /* set the host assignment address */
+    if(udev->conn_state == USB_CONN_STATE_ADDRESSED && udev->device_addr > 0)
+    {
+      usb_set_address(udev->usb_reg, udev->device_addr);
+      udev->device_addr = 0;
+    }
+  }
+  else
+  {
+    /* get endpoint register and out transfer info struct */
+    ept_info = &udev->ept_out[ept_num];
+    
+    if((ept_val & USB_SETUPTC) != 0)
+    {
+      /* endpoint setup interrupt request */
+      
+      /* get endpoint received data length */
+      ept_info->trans_len = USB_GET_RX_LEN(ept_num);
+      
+      /* read endpoint received data */
+      usb_read_packet(udev->setup_buffer, ept_info->rx_addr, ept_info->trans_len);
+      
+      /* clear endpoint rx tc flag */
+      USB_CLEAR_RXTC(USB_EPT0);
+      
+      /* endpoint setup complete handler */
+      usbd_core_setup_handler(udev, ept_num);
+    }
+    else if(ept_val & USB_RXTC )
+    {
+      /* endpoint out interrupt request */
+      USB_CLEAR_RXTC(ept_num);
+      
+      if(ept_info->is_double_buffer == 0)
+      {
+        /* get endpoint received data length */
+        length = USB_GET_RX_LEN(ept_num);
+        
+        /* read endpoint received data */
+        usb_read_packet(ept_info->trans_buf, ept_info->rx_addr, length);
+      }
+      else
+      {
+        if( ept_val & USB_RXDTS)
+        {
+          length = USB_DBUF0_GET_LEN(ept_num);
+          usb_read_packet(ept_info->trans_buf, ept_info->tx_addr, length);
+        }
+        else
+        {
+          length = USB_DBUF1_GET_LEN(ept_num);
+          usb_read_packet(ept_info->trans_buf, ept_info->rx_addr, length);
+        }
+        USB_FREE_DB_USER_BUFFER(ept_num, DATA_TRANS_OUT);
+      }
+      
+      /* set received data length */
+      ept_info->trans_len += length;
+      ept_info->trans_buf += length;
+      
+      if(ept_info->total_len == 0 || length < ept_info->maxpacket || ept_num == USB_EPT0)
+      {
+        /* out transfer complete */
+        usbd_core_out_handler(udev, ept_num);
+      }
+      else
+      {
+        /* endpoint continue receive data  */
+        usbd_ept_recv(udev, ept_num, ept_info->trans_buf, ept_info->total_len);
+      }
+    }
+  }
+}
+
+/**
+  * @brief  usb device endpoint loop handler.
+  * @param  udev: to the structure of usbd_core_type
+  * @retval none
+  */
+
+
+void usbd_ept_loop_handler(usbd_core_type *udev)
+{
+  usbd_type *usbx = udev->usb_reg;
+  usb_ept_number_type ept_num = USB_EPT0;
+  uint32_t sts_val;
+  
+  while((sts_val = usbx->intsts) & USB_TC_FLAG)
+  {
+    /* get the interrupt endpoint number */   
+    ept_num = (usb_ept_number_type)(sts_val & USB_EPT_NUM_FLAG);
+    usbd_eptn_handler(udev, ept_num);
+  }
+}
+
+/**
+  * @brief  usb device reset interrupt request handler.
+  * @param  udev: to the structure of usbd_core_type
+  * @retval none
+  */
+void usbd_reset_handler(usbd_core_type *udev)
+{
+  /* free usb buffer */
+  usb_buffer_free();
+  
+  usbd_ept_defaut_init(udev);
+#ifndef USB_EPT_AUTO_MALLOC_BUFFER
+  usbd_ept_buf_custom_define(udev, 0x80, EPT0_TX_ADDR);
+  usbd_ept_buf_custom_define(udev, 0x00, EPT0_RX_ADDR);
+#endif
+
+  usbd_ept_buf_custom_define(udev, RNDIS_NOTIFICATION_IN_EP, EPT2_TX_ADDR);
+  usbd_ept_buf_custom_define(udev, RNDIS_DATA_IN_EP, EPT1_TX_ADDR);
+  usbd_ept_buf_custom_define(udev, RNDIS_DATA_OUT_EP, EPT1_RX_ADDR);
+  
+  /* open endpoint 0 out */
+  usbd_ept_open(udev, 0x00, EPT_CONTROL_TYPE, 0x40);
+  
+  /* open endpoint 0 in */
+  usbd_ept_open(udev, 0x80, EPT_CONTROL_TYPE, 0x40);
+  
+  /* set device address to 0 */
+  usb_set_address(udev->usb_reg, 0);
+  
+  /* usb connect state set to default */
+  udev->conn_state = USB_CONN_STATE_DEFAULT;
+}
+
+/**
+  * @brief  usb device sof interrupt request handler.
+  * @param  udev: to the structure of usbd_core_type
+  * @retval none
+  */
+void usbd_sof_handler(usbd_core_type *udev)
+{
+  /* user sof handler in class define*/
+  if(udev->class_handler->sof_handler)
+    udev->class_handler->sof_handler(udev);
+}
+
+/**
+  * @brief  usb device suspend interrupt request handler.
+  * @param  udev: to the structure of usbd_core_type
+  * @retval none
+  */
+void usbd_suspend_handler(usbd_core_type *udev)
+{
+  /* save connect state */
+  udev->old_conn_state = udev->conn_state;
+  
+  /* set current state to suspend */
+  udev->conn_state = USB_CONN_STATE_SUSPENDED;
+  
+  /* enter suspend mode */
+  usbd_enter_suspend(udev);
+  
+  /* user define suspend event */
+  if(udev->class_handler->event_handler)
+    udev->class_handler->event_handler(udev, USBD_SUSPEND_EVENT);
+}
+
+/**
+  * @brief  usb device wakup interrupt request handler.
+  * @param  udev: to the structure of usbd_core_type
+  * @retval none
+  */
+void usbd_wakeup_handler(usbd_core_type *udev)
+{
+  /* exit suspend mode */
+  usb_exit_suspend(udev->usb_reg);
+  
+  /* restore connect state */
+  udev->conn_state = udev->old_conn_state;
+  
+  /* user define wakeup event */
+  if(udev->class_handler->event_handler)
+    udev->class_handler->event_handler(udev, USBD_WAKEUP_EVENT);
+}
+
+/**
+  * @}
+  */ 
+
+/**
+  * @}
+  */
+
+/**
+  * @}
+  */ 

+ 558 - 565
libs/thirdparty/rndis/usbd_drivers/src/usbd_sdr.c → libs/artery/usb/src/usbd_sdr.c

@@ -1,565 +1,558 @@
-/**
-  **************************************************************************
-  * @file     usbd_sdr.c
-  * @version  v2.0.6
-  * @date     2021-12-31
-  * @brief    usb standard device request
-  **************************************************************************
-  *                       Copyright notice & Disclaimer
-  *
-  * The software Board Support Package (BSP) that is made available to 
-  * download from Artery official website is the copyrighted work of Artery. 
-  * Artery authorizes customers to use, copy, and distribute the BSP 
-  * software and its related documentation for the purpose of design and 
-  * development in conjunction with Artery microcontrollers. Use of the 
-  * software is governed by this copyright notice and the following disclaimer.
-  *
-  * THIS SOFTWARE IS PROVIDED ON "AS IS" BASIS WITHOUT WARRANTIES,
-  * GUARANTEES OR REPRESENTATIONS OF ANY KIND. ARTERY EXPRESSLY DISCLAIMS,
-  * TO THE FULLEST EXTENT PERMITTED BY LAW, ALL EXPRESS, IMPLIED OR
-  * STATUTORY OR OTHER WARRANTIES, GUARANTEES OR REPRESENTATIONS,
-  * INCLUDING BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
-  * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
-  *
-  **************************************************************************
-  */
-#include "usbd_sdr.h"
-#include "common_config.h"
-#include <stdio.h>
-
-/** @addtogroup AT32F403A_407_middlewares_usbd_drivers
-  * @{
-  */
-  
-/** @defgroup USBD_drivers_standard_request
-  * @brief usb device standard_request
-  * @{
-  */  
-
-/** @defgroup USBD_sdr_private_functions
-  * @{
-  */
-
-static usb_sts_type usbd_get_descriptor(usbd_core_type *udev);
-static usb_sts_type usbd_set_address(usbd_core_type *udev);
-static usb_sts_type usbd_get_status(usbd_core_type *udev);
-static usb_sts_type usbd_clear_feature(usbd_core_type *udev);
-static usb_sts_type usbd_set_feature(usbd_core_type *udev);
-static usb_sts_type usbd_get_configuration(usbd_core_type *udev);
-static usb_sts_type usbd_set_configuration(usbd_core_type *udev);
-
-/**
-  * @brief  usb parse standard setup request
-  * @param  setup: setup structure
-  * @param  buf: setup buffer
-  * @retval none                            
-  */
-void usbd_setup_request_parse(usb_setup_type *setup, uint8_t *buf)
-{
-  setup->bmRequestType         = *(uint8_t *) buf;
-  setup->bRequest              = *(uint8_t *) (buf + 1);
-  setup->wValue                = SWAPBYTE(buf + 2);
-  setup->wIndex                = SWAPBYTE(buf + 4);
-  setup->wLength               = SWAPBYTE(buf + 6);
-}
-
-/**
-  * @brief  get usb standard device description request
-  * @param  udev: to the structure of usbd_core_type
-  * @retval status of usb_sts_type                           
-  */
-static usb_sts_type usbd_get_descriptor(usbd_core_type *udev)
-{
-  usb_sts_type ret = USB_OK;
-  uint16_t len = 0;
-  usbd_desc_t *desc = NULL;
-  uint8_t desc_type = udev->setup.wValue >> 8;
-  //printf("desc_type: %d\r\n", desc_type);
-  switch(desc_type)
-  {
-    case USB_DESCIPTOR_TYPE_DEVICE:
-      desc = udev->desc_handler->get_device_descriptor();
-      len = desc->length;
-      
-      UDBG printf("device status: %u\r\n", udev->conn_state);
-      
-      if ((udev->setup.wLength == 64) || (udev->conn_state == USB_CONN_STATE_DEFAULT))
-        len = 8;
-        
-      break;
-    case USB_DESCIPTOR_TYPE_CONFIGURATION:
-      desc = udev->desc_handler->get_device_configuration();
-      len = desc->length;
-      break;
-    case USB_DESCIPTOR_TYPE_STRING:
-    {
-      uint8_t str_desc = (uint8_t)udev->setup.wValue;
-      //printf("str_desc: %d\r\n", str_desc);
-      switch(str_desc)
-      {
-        case USB_LANGID_STRING:
-          desc = udev->desc_handler->get_device_lang_id();
-          len = desc->length;
-          break;
-        case USB_MFC_STRING:
-          desc = udev->desc_handler->get_device_manufacturer_string();
-          len = desc->length;
-          break;
-        case USB_PRODUCT_STRING:
-          desc = udev->desc_handler->get_device_product_string();
-          len = desc->length;
-          break;
-        case USB_SERIAL_STRING:
-          desc = udev->desc_handler->get_device_serial_string();
-          len = desc->length;
-          break;
-        case USB_CONFIG_STRING:
-          desc = udev->desc_handler->get_device_config_string();
-          len = desc->length;
-          break;
-        case USB_INTERFACE_STRING:
-          desc = udev->desc_handler->get_device_interface_string();
-          len = desc->length;
-          break;
-        default:
-          //usbd_ctrl_unsupport(udev);
-          udev->class_handler->setup_handler(udev, &udev->setup);
-          return ret;
-      }
-      break;
-    }
-    case USB_DESCIPTOR_TYPE_DEVICE_QUALIFIER:
-      usbd_ctrl_unsupport(udev);
-      //desc->length = 0;
-      //desc = udev->desc_handler->get_device_qualifier();
-      break;
-    case USB_DESCIPTOR_TYPE_OTHER_SPEED:
-      usbd_ctrl_unsupport(udev);
-      return ret;
-    default:
-      usbd_ctrl_unsupport(udev);
-      return ret;
-  }
-  
-  if(desc != NULL)
-  {
- /*   printf("len: %d\r\n", desc->length);
-  for(uint32_t i = 0; i < desc->length; i++)
-    printf("descriptor: %x\r\n", desc->descriptor[i]);*/
-    //if((desc->length != 0) && (udev->setup.wLength != 0))
-    if((len != 0) && (udev->setup.wLength != 0))
-    {
-      //len = MIN(desc->length , udev->setup.wLength);
-      len = MIN(len, udev->setup.wLength);
-      
-      //if (desc_type == USB_DESCIPTOR_TYPE_DEVICE)
-      UDBG printf("Len: %u\r\n", len);
-        
-      usbd_ctrl_send(udev, desc->descriptor, len);
-    }
-  }
-  return ret;
-}
-
-/**
-  * @brief  this request sets the device address
-  * @param  udev: to the structure of usbd_core_type
-  * @retval status of usb_sts_type                            
-  */
-static usb_sts_type usbd_set_address(usbd_core_type *udev)
-{
-  usb_sts_type ret = USB_OK;
-  usb_setup_type *setup = &udev->setup;
-  uint8_t dev_addr;
-  
-  /* if wIndex or wLength are non-zero, then the behavior of
-     the device is not specified
-  */
-  if(setup->wIndex == 0 && setup->wLength == 0)
-  {
-    dev_addr = (uint8_t)(setup->wValue) & 0x7f;
-    
-    /* device behavior when this request is received 
-       while the device is in the configured state is not specified.*/
-    if(udev->conn_state == USB_CONN_STATE_CONFIGURED )
-    {
-      usbd_ctrl_unsupport(udev);
-    }
-    else
-    {
-      udev->device_addr = dev_addr;
-      
-      if(dev_addr != 0)
-      {
-        udev->conn_state = USB_CONN_STATE_ADDRESSED;
-      }
-      else
-      {
-        udev->conn_state = USB_CONN_STATE_DEFAULT;
-      }
-      usbd_ctrl_send_status(udev);
-    }
-  }
-  else
-  {
-    usbd_ctrl_unsupport(udev);
-  }
-  return ret;
-}
-
-/**
-  * @brief  get usb status request
-  * @param  udev: to the structure of usbd_core_type
-  * @retval status of usb_sts_type                            
-  */
-static usb_sts_type usbd_get_status(usbd_core_type *udev)
-{
-  usb_sts_type ret = USB_OK;
-  switch(udev->conn_state)
-  {
-    case USB_CONN_STATE_ADDRESSED:
-    case USB_CONN_STATE_CONFIGURED:
-      if(udev->remote_wakup)
-      {
-        udev->config_status |= USB_CONF_REMOTE_WAKEUP;
-      }
-      usbd_ctrl_send(udev, (uint8_t *)(&udev->config_status), 2);
-      break;
-    default:
-      usbd_ctrl_unsupport(udev);
-      break;
-  }
-  return ret;
-}
-
-/**
-  * @brief  clear usb feature request
-  * @param  udev: to the structure of usbd_core_type
-  * @retval status of usb_sts_type                            
-  */
-static usb_sts_type usbd_clear_feature(usbd_core_type *udev)
-{
-  usb_sts_type ret = USB_OK;
-  usb_setup_type *setup = &udev->setup;
-  switch(udev->conn_state)
-  {
-    case USB_CONN_STATE_ADDRESSED:
-    case USB_CONN_STATE_CONFIGURED:
-      if(setup->wValue == USB_FEATURE_REMOTE_WAKEUP)
-      {
-        udev->remote_wakup = 0;
-        udev->config_status &= ~USB_CONF_REMOTE_WAKEUP;
-        udev->class_handler->setup_handler(udev, &udev->setup);
-        usbd_ctrl_send_status(udev);
-      }
-      break;
-    default:
-      usbd_ctrl_unsupport(udev);
-      break;
-  }
-  return ret;
-}
-
-/**
-  * @brief  set usb feature request
-  * @param  udev: to the structure of usbd_core_type
-  * @retval status of usb_sts_type                            
-  */
-static usb_sts_type usbd_set_feature(usbd_core_type *udev)
-{
-  usb_sts_type ret = USB_OK;
-  usb_setup_type *setup = &udev->setup;
-  if(setup->wValue == USB_FEATURE_REMOTE_WAKEUP)
-  {
-    udev->remote_wakup = 1;
-    udev->class_handler->setup_handler(udev, &udev->setup);
-    usbd_ctrl_send_status(udev);
-  }
-  return ret;
-}
-
-/**
-  * @brief  get usb configuration request
-  * @param  udev: to the structure of usbd_core_type
-  * @retval status of usb_sts_type                            
-  */
-static usb_sts_type usbd_get_configuration(usbd_core_type *udev)
-{
-  usb_sts_type ret = USB_OK;
-  usb_setup_type *setup = &udev->setup;
-  if(setup->wLength != 1)
-  {
-    usbd_ctrl_unsupport(udev);
-  }
-  else
-  {
-    switch(udev->conn_state)
-    {
-      case USB_CONN_STATE_ADDRESSED:
-        udev->default_config = 0;
-        usbd_ctrl_send(udev, (uint8_t *)(&udev->default_config), 1);
-        break;
-      case USB_CONN_STATE_CONFIGURED:
-        usbd_ctrl_send(udev, (uint8_t *)(&udev->dev_config), 1);
-        break;
-      default:
-        usbd_ctrl_unsupport(udev);
-        break;
-    }
-  }
-  return ret;
-}
-
-/**
-  * @brief  sets the usb device configuration request
-  * @param  udev: to the structure of usbd_core_type
-  * @retval status of usb_sts_type                            
-  */
-static usb_sts_type usbd_set_configuration(usbd_core_type *udev)
-{
-  usb_sts_type ret = USB_OK;
-  static uint8_t config_value;
-  usb_setup_type *setup = &udev->setup;
-  config_value = (uint8_t)setup->wValue;
-  
-  if(setup->wIndex == 0 && setup->wLength == 0)
-  {
-    switch(udev->conn_state)
-    {
-      case USB_CONN_STATE_ADDRESSED:
-        if(config_value)
-        {
-          udev->dev_config = config_value;
-          udev->conn_state = USB_CONN_STATE_CONFIGURED;
-          udev->class_handler->init_handler(udev);
-          usbd_ctrl_send_status(udev);
-        }
-        else
-        {
-          usbd_ctrl_send_status(udev);
-        }
-      
-        break;
-      case USB_CONN_STATE_CONFIGURED:
-        if(config_value == 0)
-        {
-          udev->conn_state = USB_CONN_STATE_ADDRESSED;
-          udev->dev_config = config_value;
-          udev->class_handler->clear_handler(udev);
-          usbd_ctrl_send_status(udev);   
-        }
-        else if(config_value == udev->dev_config)
-        {
-          udev->class_handler->clear_handler(udev); 
-          udev->dev_config = config_value;
-          udev->class_handler->init_handler(udev);
-          usbd_ctrl_send_status(udev);
-        }
-        else
-        {
-          usbd_ctrl_send_status(udev);
-        }
-        break;
-      default:
-        usbd_ctrl_unsupport(udev);
-        break;
-    }
-  }
-  else
-  {
-    usbd_ctrl_unsupport(udev);
-  }
-  return ret;
-}
-
-/**
-  * @brief  standard usb device requests
-  * @param  udev: to the structure of usbd_core_type
-  * @retval status of usb_sts_type                            
-  */
-usb_sts_type usbd_device_request(usbd_core_type *udev)
-{
-  usb_sts_type ret = USB_OK;
-  usb_setup_type *setup = &udev->setup;
-  if((setup->bmRequestType & USB_REQ_TYPE_RESERVED) != USB_REQ_TYPE_STANDARD)
-  {
-    udev->class_handler->setup_handler(udev, &udev->setup);
-    return ret;
-  }
-  switch(udev->setup.bRequest)
-  {
-    case USB_STD_REQ_GET_STATUS:
-      usbd_get_status(udev);
-      break;
-    case USB_STD_REQ_CLEAR_FEATURE:
-      usbd_clear_feature(udev);
-      break;
-    case USB_STD_REQ_SET_FEATURE:
-      usbd_set_feature(udev);
-      break;
-    case USB_STD_REQ_SET_ADDRESS:
-      usbd_set_address(udev);
-      break;
-    case USB_STD_REQ_GET_DESCRIPTOR:
-      usbd_get_descriptor(udev);
-      break;
-    case USB_STD_REQ_GET_CONFIGURATION:
-      usbd_get_configuration(udev);
-      break;
-    case USB_STD_REQ_SET_CONFIGURATION:
-      usbd_set_configuration(udev);
-      break;
-    default:
-      usbd_ctrl_unsupport(udev);
-      break;
-  }
-  return ret;
-}
-
-/**
-  * @brief  standard usb interface requests
-  * @param  udev: to the structure of usbd_core_type
-  * @retval status of usb_sts_type                            
-  */
-usb_sts_type usbd_interface_request(usbd_core_type *udev)
-{
-  usb_sts_type ret = USB_OK;
-  usb_setup_type *setup = &udev->setup;
-  switch(udev->conn_state)
-  {
-    case USB_CONN_STATE_CONFIGURED:
-      udev->class_handler->setup_handler(udev, &udev->setup);
-      if(setup->wLength == 0)
-      {
-        usbd_ctrl_send_status(udev);
-      }
-      break;
-    default:
-      usbd_ctrl_unsupport(udev);
-      break;
-  }
-  return ret;
-}
-
-/**
-  * @brief  standard usb endpoint requests
-  * @param  udev: to the structure of usbd_core_type
-  * @retval status of usb_sts_type                            
-  */
-usb_sts_type usbd_endpoint_request(usbd_core_type *udev)
-{
-  usb_sts_type ret = USB_OK;
-  usb_setup_type *setup = &udev->setup;
-  uint8_t ept_addr = LBYTE(setup->wIndex);
-  usb_ept_info *ept_info;
-  
-  if((setup->bmRequestType & USB_REQ_TYPE_RESERVED) == USB_REQ_TYPE_CLASS)
-  {
-    udev->class_handler->setup_handler(udev, &udev->setup);
-  }
-  switch(setup->bRequest)
-  {
-    case USB_STD_REQ_GET_STATUS:
-      switch(udev->conn_state)
-      {
-        case USB_CONN_STATE_ADDRESSED:
-          if((ept_addr & 0x7F) != 0)
-          {
-            usbd_set_stall(udev, ept_addr);
-          }
-          break;
-        case USB_CONN_STATE_CONFIGURED:
-        {
-          if((ept_addr & 0x80) != 0)
-          {
-           ept_info = &udev->ept_in[ept_addr & 0x7F]; 
-          }
-          else
-          {
-            ept_info = &udev->ept_out[ept_addr & 0x7F]; 
-          }
-          if(ept_info->stall == 1)
-          {
-            ept_info->status = 0x0001;
-          }
-          else
-          {
-            ept_info->status = 0x0000;
-          }
-          usbd_ctrl_send(udev, (uint8_t *)(&ept_info->status), 2);
-        }
-          break;
-        default:
-          usbd_ctrl_unsupport(udev);
-          break;
-      }
-      break;
-    case USB_STD_REQ_CLEAR_FEATURE:
-      switch(udev->conn_state)
-      {
-        case USB_CONN_STATE_ADDRESSED:
-          if((ept_addr != 0x00) && (ept_addr != 0x80))
-          {
-            usbd_set_stall(udev, ept_addr);
-          }
-          break;
-        case USB_CONN_STATE_CONFIGURED:
-          if(setup->wValue == USB_FEATURE_EPT_HALT)
-          {
-            if((ept_addr & 0x7F) != 0x00 )
-            {
-              usbd_clear_stall(udev, ept_addr);
-              udev->class_handler->setup_handler(udev, &udev->setup);
-            }
-            usbd_ctrl_send_status(udev);
-          }
-          break;
-        default:
-          usbd_ctrl_unsupport(udev);
-          break;
-      }
-      break;
-    case USB_STD_REQ_SET_FEATURE:
-      switch(udev->conn_state)
-      {
-        case USB_CONN_STATE_ADDRESSED:
-          if((ept_addr != 0x00) && (ept_addr != 0x80))
-          {
-            usbd_set_stall(udev, ept_addr);
-          }
-          break;
-        case USB_CONN_STATE_CONFIGURED:
-          if(setup->wValue == USB_FEATURE_EPT_HALT)
-          {
-            if((ept_addr != 0x00) && (ept_addr != 0x80))
-            {
-              usbd_set_stall(udev, ept_addr);
-            }  
-          }
-          udev->class_handler->setup_handler(udev, &udev->setup);
-          usbd_ctrl_send_status(udev);
-          break;
-        default:
-          usbd_ctrl_unsupport(udev);
-          break;
-      }
-      break;
-    default:
-      usbd_ctrl_unsupport(udev);
-      break;
-  }
-  return ret;
-}
-
-/**
-  * @}
-  */ 
-
-/**
-  * @}
-  */
-
-/**
-  * @}
-  */
-
+/**
+  **************************************************************************
+  * @file     usbd_sdr.c
+  * @version  v2.0.6
+  * @date     2021-12-31
+  * @brief    usb standard device request
+  **************************************************************************
+  *                       Copyright notice & Disclaimer
+  *
+  * The software Board Support Package (BSP) that is made available to 
+  * download from Artery official website is the copyrighted work of Artery. 
+  * Artery authorizes customers to use, copy, and distribute the BSP 
+  * software and its related documentation for the purpose of design and 
+  * development in conjunction with Artery microcontrollers. Use of the 
+  * software is governed by this copyright notice and the following disclaimer.
+  *
+  * THIS SOFTWARE IS PROVIDED ON "AS IS" BASIS WITHOUT WARRANTIES,
+  * GUARANTEES OR REPRESENTATIONS OF ANY KIND. ARTERY EXPRESSLY DISCLAIMS,
+  * TO THE FULLEST EXTENT PERMITTED BY LAW, ALL EXPRESS, IMPLIED OR
+  * STATUTORY OR OTHER WARRANTIES, GUARANTEES OR REPRESENTATIONS,
+  * INCLUDING BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
+  * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+  *
+  **************************************************************************
+  */
+#include "usbd_sdr.h"
+#include "common_config.h"
+#ifdef PRINTF_STDLIB
+#include <stdio.h>
+#endif
+#ifdef PRINTF_CUSTOM
+#include "tinystdio.h"
+#endif
+
+/** @addtogroup AT32F403A_407_middlewares_usbd_drivers
+  * @{
+  */
+  
+/** @defgroup USBD_drivers_standard_request
+  * @brief usb device standard_request
+  * @{
+  */  
+
+/** @defgroup USBD_sdr_private_functions
+  * @{
+  */
+
+static usb_sts_type usbd_get_descriptor(usbd_core_type *udev);
+static usb_sts_type usbd_set_address(usbd_core_type *udev);
+static usb_sts_type usbd_get_status(usbd_core_type *udev);
+static usb_sts_type usbd_clear_feature(usbd_core_type *udev);
+static usb_sts_type usbd_set_feature(usbd_core_type *udev);
+static usb_sts_type usbd_get_configuration(usbd_core_type *udev);
+static usb_sts_type usbd_set_configuration(usbd_core_type *udev);
+
+/**
+  * @brief  usb parse standard setup request
+  * @param  setup: setup structure
+  * @param  buf: setup buffer
+  * @retval none                            
+  */
+void usbd_setup_request_parse(usb_setup_type *setup, uint8_t *buf)
+{
+  setup->bmRequestType         = *(uint8_t *) buf;
+  setup->bRequest              = *(uint8_t *) (buf + 1);
+  setup->wValue                = SWAPBYTE(buf + 2);
+  setup->wIndex                = SWAPBYTE(buf + 4);
+  setup->wLength               = SWAPBYTE(buf + 6);
+}
+
+/**
+  * @brief  get usb standard device description request
+  * @param  udev: to the structure of usbd_core_type
+  * @retval status of usb_sts_type                           
+  */
+static usb_sts_type usbd_get_descriptor(usbd_core_type *udev)
+{
+  usb_sts_type ret = USB_OK;
+  uint16_t len = 0;
+  usbd_desc_t *desc = NULL;
+  uint8_t desc_type = udev->setup.wValue >> 8;
+  
+  switch(desc_type)
+  {
+    case USB_DESCIPTOR_TYPE_DEVICE:
+      desc = udev->desc_handler->get_device_descriptor();
+      len = desc->length;
+      
+      if ((udev->setup.wLength == 64) || (udev->conn_state == USB_CONN_STATE_DEFAULT))
+        len = 8;
+        
+      break;
+    case USB_DESCIPTOR_TYPE_CONFIGURATION:
+      desc = udev->desc_handler->get_device_configuration();
+      len = desc->length;
+      break;
+    case USB_DESCIPTOR_TYPE_STRING:
+    {
+      uint8_t str_desc = (uint8_t)udev->setup.wValue;
+      
+      switch(str_desc)
+      {
+        case USB_LANGID_STRING:
+          desc = udev->desc_handler->get_device_lang_id();
+          len = desc->length;
+          break;
+        case USB_MFC_STRING:
+          desc = udev->desc_handler->get_device_manufacturer_string();
+          len = desc->length;
+          break;
+        case USB_PRODUCT_STRING:
+          desc = udev->desc_handler->get_device_product_string();
+          len = desc->length;
+          break;
+        case USB_SERIAL_STRING:
+          desc = udev->desc_handler->get_device_serial_string();
+          len = desc->length;
+          break;
+        case USB_CONFIG_STRING:
+          desc = udev->desc_handler->get_device_config_string();
+          len = desc->length;
+          break;
+        case USB_INTERFACE_STRING:
+          desc = udev->desc_handler->get_device_interface_string();
+          len = desc->length;
+          break;
+        default:
+          usbd_ctrl_unsupport(udev);
+          return ret;
+      }
+      break;
+    }
+    case USB_DESCIPTOR_TYPE_DEVICE_QUALIFIER:
+      usbd_ctrl_unsupport(udev);
+      break;
+    case USB_DESCIPTOR_TYPE_OTHER_SPEED:
+      usbd_ctrl_unsupport(udev);
+      return ret;
+    default:
+      usbd_ctrl_unsupport(udev);
+      return ret;
+  }
+  
+  if(desc != NULL)
+  {
+    if((len != 0) && (udev->setup.wLength != 0))
+    {
+      len = MIN(len, udev->setup.wLength);
+      usbd_ctrl_send(udev, desc->descriptor, len);
+    }
+  }
+  return ret;
+}
+
+/**
+  * @brief  this request sets the device address
+  * @param  udev: to the structure of usbd_core_type
+  * @retval status of usb_sts_type                            
+  */
+static usb_sts_type usbd_set_address(usbd_core_type *udev)
+{
+  usb_sts_type ret = USB_OK;
+  usb_setup_type *setup = &udev->setup;
+  uint8_t dev_addr;
+  
+  /* if wIndex or wLength are non-zero, then the behavior of
+     the device is not specified
+  */
+  if(setup->wIndex == 0 && setup->wLength == 0)
+  {
+    dev_addr = (uint8_t)(setup->wValue) & 0x7f;
+    
+    /* device behavior when this request is received 
+       while the device is in the configured state is not specified.*/
+    if(udev->conn_state == USB_CONN_STATE_CONFIGURED )
+    {
+      usbd_ctrl_unsupport(udev);
+    }
+    else
+    {
+      udev->device_addr = dev_addr;
+      
+      if(dev_addr != 0)
+      {
+        udev->conn_state = USB_CONN_STATE_ADDRESSED;
+      }
+      else
+      {
+        udev->conn_state = USB_CONN_STATE_DEFAULT;
+      }
+      usbd_ctrl_send_status(udev);
+    }
+  }
+  else
+  {
+    usbd_ctrl_unsupport(udev);
+  }
+  return ret;
+}
+
+/**
+  * @brief  get usb status request
+  * @param  udev: to the structure of usbd_core_type
+  * @retval status of usb_sts_type                            
+  */
+static usb_sts_type usbd_get_status(usbd_core_type *udev)
+{
+  usb_sts_type ret = USB_OK;
+  switch(udev->conn_state)
+  {
+    case USB_CONN_STATE_ADDRESSED:
+    case USB_CONN_STATE_CONFIGURED:
+      if(udev->remote_wakup)
+      {
+        udev->config_status |= USB_CONF_REMOTE_WAKEUP;
+      }
+      usbd_ctrl_send(udev, (uint8_t *)(&udev->config_status), 2);
+      break;
+    default:
+      usbd_ctrl_unsupport(udev);
+      break;
+  }
+  return ret;
+}
+
+/**
+  * @brief  clear usb feature request
+  * @param  udev: to the structure of usbd_core_type
+  * @retval status of usb_sts_type                            
+  */
+static usb_sts_type usbd_clear_feature(usbd_core_type *udev)
+{
+  usb_sts_type ret = USB_OK;
+  usb_setup_type *setup = &udev->setup;
+  switch(udev->conn_state)
+  {
+    case USB_CONN_STATE_ADDRESSED:
+    case USB_CONN_STATE_CONFIGURED:
+      if(setup->wValue == USB_FEATURE_REMOTE_WAKEUP)
+      {
+        udev->remote_wakup = 0;
+        udev->config_status &= ~USB_CONF_REMOTE_WAKEUP;
+        udev->class_handler->setup_handler(udev, &udev->setup);
+        usbd_ctrl_send_status(udev);
+      }
+      break;
+    default:
+      usbd_ctrl_unsupport(udev);
+      break;
+  }
+  return ret;
+}
+
+/**
+  * @brief  set usb feature request
+  * @param  udev: to the structure of usbd_core_type
+  * @retval status of usb_sts_type                            
+  */
+static usb_sts_type usbd_set_feature(usbd_core_type *udev)
+{
+  usb_sts_type ret = USB_OK;
+  usb_setup_type *setup = &udev->setup;
+  if(setup->wValue == USB_FEATURE_REMOTE_WAKEUP)
+  {
+    udev->remote_wakup = 1;
+    udev->class_handler->setup_handler(udev, &udev->setup);
+    usbd_ctrl_send_status(udev);
+  }
+  return ret;
+}
+
+/**
+  * @brief  get usb configuration request
+  * @param  udev: to the structure of usbd_core_type
+  * @retval status of usb_sts_type                            
+  */
+static usb_sts_type usbd_get_configuration(usbd_core_type *udev)
+{
+  usb_sts_type ret = USB_OK;
+  usb_setup_type *setup = &udev->setup;
+  if(setup->wLength != 1)
+  {
+    usbd_ctrl_unsupport(udev);
+  }
+  else
+  {
+    switch(udev->conn_state)
+    {
+      case USB_CONN_STATE_ADDRESSED:
+        udev->default_config = 0;
+        usbd_ctrl_send(udev, (uint8_t *)(&udev->default_config), 1);
+        break;
+      case USB_CONN_STATE_CONFIGURED:
+        usbd_ctrl_send(udev, (uint8_t *)(&udev->dev_config), 1);
+        break;
+      default:
+        usbd_ctrl_unsupport(udev);
+        break;
+    }
+  }
+  return ret;
+}
+
+/**
+  * @brief  sets the usb device configuration request
+  * @param  udev: to the structure of usbd_core_type
+  * @retval status of usb_sts_type                            
+  */
+static usb_sts_type usbd_set_configuration(usbd_core_type *udev)
+{
+  usb_sts_type ret = USB_OK;
+  static uint8_t config_value;
+  usb_setup_type *setup = &udev->setup;
+  config_value = (uint8_t)setup->wValue;
+  
+  if(setup->wIndex == 0 && setup->wLength == 0)
+  {
+    switch(udev->conn_state)
+    {
+      case USB_CONN_STATE_ADDRESSED:
+        if(config_value)
+        {
+          udev->dev_config = config_value;
+          udev->conn_state = USB_CONN_STATE_CONFIGURED;
+          udev->class_handler->init_handler(udev);
+          usbd_ctrl_send_status(udev);
+        }
+        else
+        {
+          usbd_ctrl_send_status(udev);
+        }
+      
+        break;
+      case USB_CONN_STATE_CONFIGURED:
+        if(config_value == 0)
+        {
+          udev->conn_state = USB_CONN_STATE_ADDRESSED;
+          udev->dev_config = config_value;
+          udev->class_handler->clear_handler(udev);
+          usbd_ctrl_send_status(udev);   
+        }
+        else if(config_value == udev->dev_config)
+        {
+          udev->class_handler->clear_handler(udev); 
+          udev->dev_config = config_value;
+          udev->class_handler->init_handler(udev);
+          usbd_ctrl_send_status(udev);
+        }
+        else
+        {
+          usbd_ctrl_send_status(udev);
+        }
+        break;
+      default:
+        usbd_ctrl_unsupport(udev);
+        break;
+    }
+  }
+  else
+  {
+    usbd_ctrl_unsupport(udev);
+  }
+  return ret;
+}
+
+/**
+  * @brief  standard usb device requests
+  * @param  udev: to the structure of usbd_core_type
+  * @retval status of usb_sts_type                            
+  */
+usb_sts_type usbd_device_request(usbd_core_type *udev)
+{
+  usb_sts_type ret = USB_OK;
+  usb_setup_type *setup = &udev->setup;
+
+  if((setup->bmRequestType & USB_REQ_TYPE_RESERVED) != USB_REQ_TYPE_STANDARD)
+  {
+    udev->class_handler->setup_handler(udev, &udev->setup);
+    return ret;
+  }
+  switch(udev->setup.bRequest)
+  {
+    case USB_STD_REQ_GET_STATUS:
+      usbd_get_status(udev);
+      break;
+    case USB_STD_REQ_CLEAR_FEATURE:
+      usbd_clear_feature(udev);
+      break;
+    case USB_STD_REQ_SET_FEATURE:
+      usbd_set_feature(udev);
+      break;
+    case USB_STD_REQ_SET_ADDRESS:
+      usbd_set_address(udev);
+      break;
+    case USB_STD_REQ_GET_DESCRIPTOR:
+      usbd_get_descriptor(udev);
+      break;
+    case USB_STD_REQ_GET_CONFIGURATION:
+      usbd_get_configuration(udev);
+      break;
+    case USB_STD_REQ_SET_CONFIGURATION:
+      usbd_set_configuration(udev);
+      break;
+    default:
+      usbd_ctrl_unsupport(udev);
+      break;
+  }
+  return ret;
+}
+
+/**
+  * @brief  standard usb interface requests
+  * @param  udev: to the structure of usbd_core_type
+  * @retval status of usb_sts_type                            
+  */
+usb_sts_type usbd_interface_request(usbd_core_type *udev)
+{
+  usb_sts_type ret = USB_OK;
+  usb_setup_type *setup = &udev->setup;
+  
+  switch(udev->conn_state)
+  {
+    case USB_CONN_STATE_CONFIGURED:
+      udev->class_handler->setup_handler(udev, &udev->setup);
+      if(setup->wLength == 0)
+      {
+        usbd_ctrl_send_status(udev);
+      }
+      break;
+    default:
+      usbd_ctrl_unsupport(udev);
+      break;
+  }
+  return ret;
+}
+
+/**
+  * @brief  standard usb endpoint requests
+  * @param  udev: to the structure of usbd_core_type
+  * @retval status of usb_sts_type                            
+  */
+usb_sts_type usbd_endpoint_request(usbd_core_type *udev)
+{
+  usb_sts_type ret = USB_OK;
+  usb_setup_type *setup = &udev->setup;
+  uint8_t ept_addr = LBYTE(setup->wIndex);
+  usb_ept_info *ept_info;
+  
+  if((setup->bmRequestType & USB_REQ_TYPE_RESERVED) == USB_REQ_TYPE_CLASS)
+  {
+    udev->class_handler->setup_handler(udev, &udev->setup);
+  }
+  switch(setup->bRequest)
+  {
+    case USB_STD_REQ_GET_STATUS:
+      switch(udev->conn_state)
+      {
+        case USB_CONN_STATE_ADDRESSED:
+          if((ept_addr & 0x7F) != 0)
+          {
+            usbd_set_stall(udev, ept_addr);
+          }
+          break;
+        case USB_CONN_STATE_CONFIGURED:
+        {
+          if((ept_addr & 0x80) != 0)
+          {
+           ept_info = &udev->ept_in[ept_addr & 0x7F]; 
+          }
+          else
+          {
+            ept_info = &udev->ept_out[ept_addr & 0x7F]; 
+          }
+          if(ept_info->stall == 1)
+          {
+            ept_info->status = 0x0001;
+          }
+          else
+          {
+            ept_info->status = 0x0000;
+          }
+          usbd_ctrl_send(udev, (uint8_t *)(&ept_info->status), 2);
+        }
+          break;
+        default:
+          usbd_ctrl_unsupport(udev);
+          break;
+      }
+      break;
+    case USB_STD_REQ_CLEAR_FEATURE:
+      switch(udev->conn_state)
+      {
+        case USB_CONN_STATE_ADDRESSED:
+          if((ept_addr != 0x00) && (ept_addr != 0x80))
+          {
+            usbd_set_stall(udev, ept_addr);
+          }
+          break;
+        case USB_CONN_STATE_CONFIGURED:
+          if(setup->wValue == USB_FEATURE_EPT_HALT)
+          {
+            if((ept_addr & 0x7F) != 0x00 )
+            {
+              usbd_clear_stall(udev, ept_addr);
+              udev->class_handler->setup_handler(udev, &udev->setup);
+            }
+            usbd_ctrl_send_status(udev);
+          }
+          break;
+        default:
+          usbd_ctrl_unsupport(udev);
+          break;
+      }
+      break;
+    case USB_STD_REQ_SET_FEATURE:
+      switch(udev->conn_state)
+      {
+        case USB_CONN_STATE_ADDRESSED:
+          if((ept_addr != 0x00) && (ept_addr != 0x80))
+          {
+            usbd_set_stall(udev, ept_addr);
+          }
+          break;
+        case USB_CONN_STATE_CONFIGURED:
+          if(setup->wValue == USB_FEATURE_EPT_HALT)
+          {
+            if((ept_addr != 0x00) && (ept_addr != 0x80))
+            {
+              usbd_set_stall(udev, ept_addr);
+            }  
+          }
+          udev->class_handler->setup_handler(udev, &udev->setup);
+          usbd_ctrl_send_status(udev);
+          break;
+        default:
+          usbd_ctrl_unsupport(udev);
+          break;
+      }
+      break;
+    default:
+      usbd_ctrl_unsupport(udev);
+      break;
+  }
+  return ret;
+}
+
+/**
+  * @}
+  */ 
+
+/**
+  * @}
+  */
+
+/**
+  * @}
+  */
+

BIN
libs/thirdparty/LwIP/.DS_Store


+ 3349 - 4548
libs/thirdparty/lwip_2.1.2/CHANGELOG → libs/thirdparty/LwIP/CHANGELOG

@@ -1,4548 +1,3349 @@
-HISTORY
- * These are only the most important changes. For a full list, use git log:
-   http://git.savannah.nongnu.org/cgit/lwip.git
-
-(git master)
-
-  * [Enter new changes just after this line - do not remove this line]
-
-(STABLE-2.1.2):
-
-  ++ Bugfixes:
-
-  2018-11-21: Jens Nielsen
-  * netbiosns.c: fix expecting too large packet (bug #55069)
-
-  2018-11-19: Dirk Ziegelmeier
-  * smtp.c: fix compiling with strict C compatibility because of strnlen (bug #55034)
-
-  2018-11-12: Simon Goldschmidt
-  * tcp.c: fix overflow check in tcp_recved triggering invalid assertion (bug #55015)
-
-  2018-11-12: Simon Goldschmidt
-  * tcp.c: fix a bug in sending RST segments (sent from port 0)
-
-(STABLE-2.1.1):
-
-  ++ Bugfixes:
-
-  2018-11-01: Joan Lledó
-  * sockets.c: fix bad assertion in lwip_poll_dec_sockets_used() (bug #54933)
-
-  2018-11-01: Dirk Ziegelmeier
-  * ip4.c: don't send 127.* to default netif (bug #54670)
-
-  2018-10-23: David Girault
-  * altcp_tls_mbedtls.c: fix use-after free (bug #54774)
-
-  2018-10-23: Ognjen Bjelica, Dirk Ziegelmeier
-  * snmp_scalar.c: Avoid NULL pointer dereference (bug #54886)
-
-  2018-10-23: Simon Goldschmidt
-  * Fix missing standard includes in multiple files
-
-  2018-10-17: Ivan Warren
-  * def.h: fix casting htonX and ntohX to u16_t (bug #54850)
-
-  2018-10-12: Simon Goldschmidt
-  * Revert "tcp_abandon: no need to buffer pcb->local_port" (fix that source port was 0 for RST
-    called when aborting a connection)
-
-  2018-10-11: Jonas Rabenstein
-  * tcp.c: tcp_recved: check for overflow and warn about too big values (patch #9699)              
-
-  2018-10-06: Joan Lledó
-  * sockets.c: alloc_socket(): Check for LWIP_SOCKET_POLL when setting select-
-    related variables (patch #9696)
-
-  2018-10-04: Spencer
-  * tcp.c: Update prev pointer when skipping entries in tcp_slowtmr (patch #9694)
-
-  2018-09-27: Martine Lenders
-  * lowpan6.c: Fix IEEE 802.15.4 address setting (bug #54749)
-
-(STABLE-2.1.0):
-
-  ++ New features:
-
-  2018-06-17: Simon Goldschmidt
-  * lwiperf: implemented iPerf client mode
-
-  2018-04-23: Dirk Ziegelmeier
-  * added cmake build files
-
-  2018-03-04: Ray Abram
-  * netbios responder: respond to '*' queries
-
-  2018-02-23: Benjamin Aigner
-  * 6lowpan: add 6lowpan-over-BLE netif (based on existing 6lowpan netif)
-
-  2018-02-22: Simon Goldschmidt
-  * ipv6: add support for stateless DHCPv6 (to get DNS servers in SLAAC nets)
-
-  2018-02-16: Simon Goldschmidt
-  * add raw API http(s) client (with proxy support)
-
-  2018-02-01: Simon Goldschmidt
-  * tcp: add hooks to implement additional socket options
-
-  2018-02-01: Simon Goldschmidt
-  * tcp: add hooks to implement tcp md5 signatures or similar (see contrib/addons for an example)
-
-  2018-01-05: Simon Goldschmidt
-  * Added sys_mbox_trypost_fromisr() and tcpip_callbackmsg_trycallback_fromisr()
-    These can be used to post preallocated messages from an ISR to the tcpip thread
-    (e.g. when using FreeRTOS)
-
-  2018-01-02: Dirk Ziegelmeier
-  * task #14780: Add debug helper asserts to ensure threading/locking requirements are met
-
-  2017-11-21: Simon Goldschmidt
-  * task #14600: tcp_alloc(): kill TF_CLOSEPEND connections before other ESTABLISHED
-
-  2017-11-21: Simon Goldschmidt
-  * makefsdata: added option "-ssi:<filename>" to control SSI tag checking/insertion
-    through a list of filenames, not by checking the file extension at runtime
-
-  2017-11-20: Joel Cunningham
-  * netconn: add LWIP_HOOK_NETCONN_EXTERNAL_RESOLVE to use external DNS resolver (patch #9427)
-
-  2017-11-14: Joel Cunningham
-  * netifapi: Add thread safe ARP cache APIs (task #14724)
-
-  2017-11-06: Axel Lin
-  * TCP: kill existing connections with a LOWER priority than the one currently being opened.
-    Previous implementations also kill existing connections of the SAME priority.
-
-  2017-09-21: Kalle Olavi Niemitalo
-  * sockets: add poll() implementation (patch #9450)
-
-  2017-09-10: Joel Cunningham
-  * sockets: add readv() implementation (task #14610)
-
-  2017-08-04: Simon Goldschmidt
-  * Clean up DHCP a bit: no need keep msg_out and msg_in as members in struct
-    dhcp - they are used in a call stack only (p_out and options_out_len as well)
-
-  2017-08-04: Simon Goldschmidt
-  * pbuf: split pbuf_header(s16_t) into pbuf_add_header(size_t) and
-    pbuf_remove_header(size_t)
-
-  2017-07-20: Douglas
-  * sys: deprecate sys_arch_sem_wait and sys_arch_mbox_fetch returning the
-    time waited rather they are now defined to return != SYS_ARCH_TIMEOUT
-    on success.
-
-  2017-07-03: Jakub Schmidtke
-  * tcp: added support for sending TCP SACKs
-
-  2017-06-20: Joel Cunningham
-  * netconn/netdb: added core locking support to netconn_gethostbyname (task #14523)
-
-  2017-04-25: Simon Goldschmidt
-  * dhcp: added two hooks for adding and parsing user defined DHCP options
-
-  2017-04-25: Joel Cunningham
-  * sockets: added recvmsg for UDP (together with CMSG and IP_PKTINFO) (task #14247)
-
-  2017-04-20: Joel Cunningham
-  * tcp: added Appropriate Byte Counting support (task #14128)
-
-  2017-04-11: Simon Goldschmidt
-  * netconn/sockets: remove fatal error handling, fix asynchronous error handling,
-    ensure data before RST can be received
-
-  2017-03-30: Simon Goldschmidt
-  * added "application layered TCP" connection API (altcp) for seamless integration
-    of TLS or proxy connections
-
-  2017-03-09: Simon Goldschmidt
-  * sockets: add recvmsg for TCP
-
-  2017-03-02: Joel Cunningham
-  * netconn/sockets: vectorize netconn_write for TCP, treating a vectored I/O write
-    atomically in regards to TCP segmentation (patch #8882)
-
-  2017-03-02: Simon Goldschmidt
-  * netconn: added nonblocking accept/recv to netconn API (task #14396)
-
-  2017-02-28: Simon Goldschmidt
-  * Added LWIP_SINGLE_NETIF for small targets with only one netif
-
-  2017-02-10: David van Moolenbroek
-  * Implement UDP and RAW multicast support for IPv6 (core API, not netconn/sockets)
-
-  2017-02-04: David van Moolenbroek
-  * IPv6 scopes support
-
-  2017-01-20: Joel Cunningham
-  * sockets: add interface name/index APIs (task #14314)
-  
-  2017-01-08: David van Moolenbroek
-  * Extensions to RAW API (patch #9208)
-    - Connected RAW PCBs
-    - Add raw_sendto_if_src()
-    - Support IP_HDRINCL socket option
-
-  ++ Bugfixes:
-
-  2018-06-19: Simon Goldschmidt
-  * tcp: fix RTO timer not working if link is down
-
-  2018-06-15: Sylvain Rochet
-  * ppp: multiple smaller bugfixes
-
-  2018-05-17: Simon Goldschmidt
-  * etharp: arp table can now be bigger than 127 entries
-
-  2018-04-25: Jens Nielsen
-  * tftp server: correctly handle retransmissions
-
-  2018-04-18: Simon Goldschmidt
-  sockets: fix race conditions when closing full-duplex sockets
-
-  2018-03-09: Simon Goldschmidt
-  * 6lowpan: fix to work against contiki; added ZigBee encapsulation netif for tests
-
-  2018-02-04: Simon Goldschmidt
-  * sockets: fix inconsistencies on close (inconsistent error codes, double FIN)
-
-  2018-01-05: Dirk Ziegelmeier
-  * Fix bug #52748: the bug in timeouts.c by reimplementing timer logic to use
-    absolute instead of relative timeout values
-
-  2017-12-31: Dirk Ziegelmeier
-  * Fix bug #52704: DHCP and bad OFFER: Stop timeout only if offer is accepted
-
-  2017-11-08: Joel Cunningham
-  * netif: ensure link and admin states are up in issue reports (bug #52353)
-
-  2017-09-12: David Lockyer
-  * select: allocate select_cb from memp for LWIP_MPU_COMPATIBLE = 1 (bug #51990)
-
-  2017-09-11: Simon Goldschmidt
-  * tcp_in.c: fix bug #51937 (leaking tcp_pcbs on passive close with unacked data)
-
-  2017-08-11: Joel Cunningham
-  * lwip_itoa: fix converting the number 0 (previously converted to '\0') (bug #51729)
-
-  2017-08-08: Dirk Ziegelmeier
-  * ip4_route_src: parameter order is reversed: ip4_route_src(dest, src) -> ip4_route_src(src, dest)
-    to make parameter order consistent with other ip*_route*() functions
-    Same also applies to LWIP_HOOK_IP4_ROUTE_SRC() parameter order.
-
-  2017-08-04: Joel Cunningham
-  * tcp: re-work persist timer to fully close window (details in bug #50837)
-
-  2017-07-26: Simon Goldschmidt
-  * snmp_msg.c: fix bug #51578 (SNMP failed to decode some values on non 32bit platforms)
-
-  2017-07-20: Simon Goldschmidt
-  * compatibility headers: moved from 'src/include/posix' to 'src/include/compat/posix',
-    'src/include/compat/stdc' etc.
-
-  2017-05-09: Joel Cunningham
-  * tcp: add zero-window probe timeout (bug #50837)
-
-  2017-04-11: Simon Goldschmidt
-  * sockets.c: task #14420 (Remove sys_sem_signal from inside SYS_ARCH_PROTECT
-    crit section) done for LWIP_TCPIP_CORE_LOCKING==1
-
-  2017-02-24: Simon Goldschmidt
-  * sockets.c: fixed close race conditions in lwip_select (for LWIP_NETCONN_FULLDUPLEX)
-
-  2017-02-24: Simon Goldschmidt
-  * sockets.c: fixed that select ignored invalid/not open sockets in the fd_sets (bug #50392)
-
-  2017-01-11: David van Moolenbroek
-  * Lots of IPv6 related fixes and improvements
-
-(STABLE-2.0.3)
-
-  ++ Bugfixes:
-
-  2017-09-11: Simon Goldschmidt
-  * tcp_in.c: fix bug #51937 (leaking tcp_pcbs on passive close with unacked data)
-
-  2017-08-02: Abroz Bizjak/Simon Goldschmidt
-  * multiple fixes in IPv4 reassembly (leading to corrupted datagrams received)
-  
-  2017-03-30: Simon Goldschmidt
-  * dhcp.c: return ERR_VAL instead of asserting on offset-out-of-pbuf
-
-  2017-03-23: Dirk Ziegelmeier
-  * dhcp.h: fix bug #50618 (dhcp_remove_struct() macro does not work)
-
-(STABLE-2.0.2)
-
-  ++ New features:
-
-  2017-02-10: Dirk Ziegelmeier
-  * Implement task #14367: Hooks need a better place to be defined:
-    We now have a #define for a header file name that is #included in every .c
-    file that provides hooks.
-
-  2017-02-10: Simon Goldschmidt
-  * tcp_close does not fail on memory error (instead, FIN is sent from tcp_tmr)
-
-  ++ Bugfixes:
-
-  2017-03-08
-  * tcp: do not keep sending SYNs when getting ACKs
-
-  2017-03-08: Joel Cunningham
-  * tcp: Initialize ssthresh to TCP_SND_BUF (bug #50476)
-
-  2017-03-01: Simon Goldschmidt
-  * httpd: LWIP_HTTPD_POST_MANUAL_WND: fixed double-free when httpd_post_data_recved
-    is called nested from httpd_post_receive_data() (bug #50424)
-
-  2017-02-28: David van Moolenbroek/Simon Goldschmidt
-  * tcp: fixed bug #50418: LWIP_EVENT_API: fix invalid calbacks for SYN_RCVD pcb
-
-  2017-02-17: Simon Goldschmidt
-  * dns: Improved DNS_LOCAL_HOSTLIST interface (bug #50325)
-
-  2017-02-16: Simon Goldschmidt
-  * LWIP_NETCONN_FULLDUPLEX: fixed shutdown during write (bug #50274)
-
-  2017-02-13: Simon Goldschmidt/Dirk Ziegelmeier
-  * For tiny targtes, LWIP_RAND is optional (fix compile time checks)
-
-  2017-02-10: Simon Goldschmidt
-  * tcp: Fixed bug #47485 (tcp_close() should not fail on memory error) by retrying
-    to send FIN from tcp_fasttmr
-
-  2017-02-09: Simon Goldschmidt
-  * sockets: Fixed bug #44032 (LWIP_NETCONN_FULLDUPLEX: select might work on
-    invalid/reused socket) by not allowing to reallocate a socket that has
-    "select_waiting != 0"
-
-  2017-02-09: Simon Goldschmidt
-  * httpd: Fixed bug #50059 (httpd LWIP_HTTPD_SUPPORT_11_KEEPALIVE vs.
-    LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED)
-
-  2017-02-08: Dirk Ziegelmeier
-  * Rename "IPv6 mapped IPv4 addresses" to their correct name from RFC4191:
-    "IPv4-mapped IPv6 address"
-
-  2017-02-08: Luc Revardel
-  * mld6.c: Fix bug #50220 (mld6_leavegroup does not send ICMP6_TYPE_MLD, even
-    if last reporter)
-
-  2017-02-08: David van Moolenbroek
-  * ip6.c: Patch #9250: fix source substitution in ip6_output_if()
-
-  2017-02-08: Simon Goldschmidt
-  * tcp_out.c: Fixed bug #50090 (last_unsent->oversize_left can become wrong value
-    in tcp_write error path)
-
-  2017-02-02: Dirk Ziegelmeier
-  * Fix bug #50206: UDP Netconn bind to IP6_ADDR_ANY fails
-
-  2017-01-18: Dirk Ziegelmeier
-  * Fix zero-copy RX, see bug bug #50064. PBUF_REFs were not supported as ARP requests.
-
-  2017-01-15: Axel Lin, Dirk Ziegelmeier
-  * minor bug fixes in mqtt
-
-  2017-01-11: Knut Andre Tidemann
-  * sockets/netconn: fix broken default ICMPv6 handling of checksums
-
-(STABLE-2.0.1)
-
-  ++ New features:
-
-  2016-12-31: Simon Goldschmidt
-  * tcp.h/.c: added function tcp_listen_with_backlog_and_err() to get the error
-    reason when listening fails (bug #49861)
-
-  2016-12-20: Erik Andersen
-  * Add MQTT client
-
-  2016-12-14: Jan Breuer:
-  * opt.h, ndc.h/.c: add support for RDNSS option (as per RFC 6106)
-
-  2016-12-14: David van Moolenbroek
-  * opt.h, nd6.c: Added LWIP_HOOK_ND6_GET_GW()
-
-  2016-12-09: Dirk Ziegelmeier
-  * ip6_frag.c: Implemented support for LWIP_NETIF_TX_SINGLE_PBUF
-
-  2016-12-09: Simon Goldschmidt
-  * dns.c: added one-shot multicast DNS queries
-
-  2016-11-24: Ambroz Bizjak, David van Moolenbroek
-  * tcp_out.c: Optimize passing contiguous nocopy buffers to tcp_write (bug #46290)
-
-  2016-11-16: Dirk Ziegelmeier
-  * sockets.c: added support for IPv6 mapped IPv4 addresses
-
-  ++ Bugfixes:
-
-  2016-12-16: Thomas Mueller
-  * api_lib.c: fixed race condition in return value of netconn_gethostbyname()
-    (and thus also lwip_gethostbyname/_r() and lwip_getaddrinfo())
-
-  2016-12-15: David van Moolenbroek
-  * opt.h, tcp: added LWIP_HOOK_TCP_ISN() to implement less predictable initial
-    sequence numbers (see contrib/addons/tcp_isn for an example implementation)
-
-  2016-12-05: Dirk Ziegelmeier
-  * fixed compiling with IPv4 disabled (IPv6 only case)
-
-  2016-11-28: Simon Goldschmidt
-  * api_lib.c: fixed bug #49725 (send-timeout: netconn_write() can return
-    ERR_OK without all bytes being written)
-
-  2016-11-28: Ambroz Bizjak
-  * tcpi_in.c: fixed bug #49717 (window size in received SYN and SYN-ACK
-    assumed scaled)
-
-  2016-11-25: Simon Goldschmidt
-  * dhcp.c: fixed bug #49676 (Possible endless loop when parsing dhcp options)
-
-  2016-11-23: Dirk Ziegelmeier
-  * udp.c: fixed bug #49662: multicast traffic is now only received on a UDP PCB
-   (and therefore on a UDP socket/netconn) when the PCB is bound to IP_ADDR_ANY
-
-  2016-11-16: Dirk Ziegelmeier
-  * *: Fixed dual-stack behaviour, IPv6 mapped IPv4 support in socket API
-
-  2016-11-14: Joel Cunningham
-  * tcp_out.c: fixed bug #49533 (start persist timer when unsent seg can't fit
-    in window) 
-
-  2016-11-16: Roberto Barbieri Carrera
-  * autoip.c: fixed bug #49610 (sometimes AutoIP fails to reuse the same address)
-
-  2016-11-11: Dirk Ziegelmeier
-  * sockets.c: fixed bug #49578 (dropping multicast membership does not work
-    with LWIP_SOCKET_OFFSET)
-
-(STABLE-2.0.0)
-
-  ++ New features:
-
-  2016-07-27: Simon Goldschmidt
-  * opt.h, timeouts.h/.c: added LWIP_TIMERS_CUSTOM to override the default
-    implementation of timeouts
-
-  2016-07-xx: Dirk Ziegelmeier
-  * Large overhaul of doxygen documentation
-
-  2016-04-05: Simon Goldschmidt
-  * timers.h/.c: prepare for overriding current timeout implementation: all
-    stack-internal caclic timers are avaliable in the lwip_cyclic_timers array
-
-  2016-03-23: Simon Goldschmidt
-  * tcp: call accept-callback with ERR_MEM when allocating a pcb fails on
-    passive open to inform the application about this error
-    ATTENTION: applications have to handle NULL pcb in accept callback!
-
-  2016-02-22: Ivan Delamer
-  * Initial 6LoWPAN support
-
-  2016-02-XX to 2016-03-XX: Dirk Ziegelmeier
-  * Cleanup TCPIP thread sync methods in a way that it is possibe to use them
-    in arbitrary code that needs things to be done in TCPIP thread. Used to
-    decouple netconn, netif, ppp and 6LoWPAN from LWIP core.
-
-  2016-02-XX: Dirk Ziegelmeier
-  * Implement dual-stack support in RAW, UDP and TCP. Add new IP address
-    type IPADDR_ANY_TYPE for this. Netconn/Socket API: Dual-stack is
-    automatically supported when an IPv6 netconn/socket is created.
-
-  2015-12-26: Martin Hentschel and Dirk Ziegelmeier
-  * Rewrite SNMP agent. SNMPv2c + MIB compiler.
-
-  2015-11-12: Dirk Ziegelmeier
-  * Decouple SNMP stack from lwIP core and move stack to apps/ directory.
-    Breaking change: Users have to call snmp_init() now!
-
-  2015-11-12: Dirk Ziegelmeier
-  * Implement possibility to declare private memory pools. This is useful to
-    decouple some apps from the core (SNMP stack) or make contrib app usage
-    simpler (httpserver_raw)
-
-  2015-10-09: Simon Goldschmidt
-  * started to move "private" header files containing implementation details to
-    "lwip/priv/" include directory to seperate the API from the implementation.
-
-  2015-10-07: Simon Goldschmidt
-  * added sntp client as first "supported" application layer protocol implementation
-    added 'apps' folder
-
-  2015-09-30: Dirk Ziegelmeier
-  * snmp_structs.h, mib_structs.c, mib2.c: snmp: fixed ugly inheritance
-    implementation by aggregating the "base class" (struct mib_node) in all
-    derived node classes to get more type-safe code
-
-  2015-09-23: Simon Goldschmidt
-  * netif.h/.c, nd6.c: task #13729: Convert netif addresses (IPv4 & IPv6) to
-    ip_addr_t (so they can be used without conversion/temporary storage)
-
-  2015-09-08: Dirk Ziegelmeier
-  * snmp: Separate mib2 counter/table callbacks from snmp agent. This both cleans
-    up the code and should allow integration of a 3rd party agent/mib2. Simple
-    counters are kept in MIB2_STATS, tree/table change function prototypes moved to
-    snmp_mib2.h.
-
-  2015-09-03: Simon Goldschmidt
-  * opt.h, dns.h/.c: DNS/IPv6: added support for AAAA records
-
-  2015-09-01: Simon Goldschmidt
-  * task #12178: hardware checksum capabilities can be configured per netif
-   (use NETIF_SET_CHECKSUM_CTRL() in your netif's init function)
-
-  2015-08-30: Simon Goldschmidt
-  * PBUF_REF with "custom" pbufs is now supported for RX pbufs (see pcapif in
-    contrib for an example, LWIP_SUPPORT_CUSTOM_PBUF is required)
-
-  2015-08-30: Simon Goldschmidt
-  * support IPv4 source based routing: define LWIP_HOOK_IP4_ROUTE_SRC to point
-    to a routing function
-
-  2015-08-05: Simon Goldschmidt
-  * many files: allow multicast socket options IP_MULTICAST_TTL, IP_MULTICAST_IF
-    and IP_MULTICAST_LOOP to be used without IGMP
-
-  2015-04-24: Simon Goldschmidt
-  * dhcp.h/c, autoip.h/.c: added functions dhcp/autoip_supplied_address() to
-    check for the source of address assignment (replacement for NETIF_FLAG_DHCP)
-
-  2015-04-10: Simon Goldschmidt
-  * many files: task #13480: added LWIP_IPV4 define - IPv4 can be disabled,
-    leaving an IPv6-only stack
-
-  2015-04-09: Simon Goldschmidt
-  * nearly all files: task #12722 (improve IPv4/v6 address handling): renamed
-    ip_addr_t to ip4_addr_t, renamed ipX_addr_t to ip_addr_t and added IP
-    version; ip_addr_t is used for all generic IP addresses for the API,
-    ip(4/6)_addr_t are only used internally or when initializing netifs or when
-    calling version-related functions
-
-  2015-03-24: Simon Goldschmidt
-  * opt.h, ip4_addr.h, ip4.c, ip6.c: loopif is not required for loopback traffic
-    any more but passed through any netif (ENABLE_LOOPBACK has to be enabled)
-
-  2015-03-23: Simon Goldschmidt
-  * opt.h, etharp.c: with ETHARP_TABLE_MATCH_NETIF== 1, duplicate (Auto)-IP
-    addresses on multiple netifs should now be working correctly (if correctly
-    addressed by routing, that is)
-
-  2015-03-23: Simon Goldschmidt
-  * etharp.c: Stable etharp entries that are about to expire are now refreshed
-    using unicast to prevent unnecessary broadcast. Only if no answer is received
-    after 15 seconds, broadcast is used.
-
-  2015-03-06: Philip Gladstone
-  * netif.h/.c: patch #8359 (Provide utility function to add an IPv6 address to
-    an interface)
-
-  2015-03-05: Simon Goldschmidt
-  * netif.c, ip4.c, dhcp.c, autoip.c: fixed bug #37068 (netif up/down handling
-    is unclear): correclty separated administrative status of a netif (up/down)
-    from 'valid address' status
-    ATTENTION: netif_set_up() now always has to be called, even when dhcp/autoip
-    is used!
-
-  2015-02-26: patch by TabascoEye
-  * netif.c, udp.h/.c: fixed bug #40753 (re-bind UDP pcbs on change of IP address)
-
-  2015-02-22: chrysn, Simon Goldschmidt
-  * *.*: Changed nearly all functions taking 'ip(X)_addr_t' pointer to take
-    const pointers (changed user callbacks: raw_recv_fn, udp_recv_fn; changed
-    port callbacks: netif_output_fn, netif_igmp_mac_filter_fn)
-
-  2015-02-19: Ivan Delamer
-  * netif.h, dhcp.c: Removed unused netif flag for DHCP. The preferred way to evaluate
-    if DHCP is active is through netif->dhcp field.
-
-  2015-02-19: Ivan Delamer
-  * netif.h, slipif.c, ppp.c: Removed unused netif flag for point to point connections
-
-  2015-02-18: Simon Goldschmidt
-  * api_lib.c: fixed bug #37958 "netconn API doesn't handle correctly
-    connections half-closed by peer"
-
-  2015-02-18: Simon Goldschmidt
-  * tcp.c: tcp_alloc() prefers killing CLOSING/LAST_ACK over active connections
-    (see bug #39565)
-
-  2015-02-16: Claudius Zingerli, Sergio Caprile
-  * opt.h, dhcp.h/.c: patch #8361 "Add support for NTP option in DHCP"
-
-  2015-02-14: Simon Goldschmidt
-  * opt.h, snmp*: added support for write-access community and dedicated
-    community for sending traps
-
-  2015-02-13: Simon Goldschmidt
-  * opt.h, memp.c: added hook LWIP_HOOK_MEMP_AVAILABLE() to get informed when
-    a memp pool was empty and an item is now available
-
-  2015-02-13: Simon Goldschmidt
-  * opt.h, pbuf.h/.c, etharp.c: Added the option PBUF_LINK_ENCAPSULATION_HLEN to
-    allocate additional header space for TX on netifs requiring additional headers
-
-  2015-02-12: chrysn
-  * timers.h/.c: introduce sys_timeouts_sleeptime (returns the time left before
-    the next timeout is due, for NO_SYS==1)
-
-  2015-02-11: Nick van Ijzendoorn
-  * opt.h, sockets.h/c: patch #7702 "Include ability to increase the socket number
-    with defined offset"
-
-  2015-02-11: Frederick Baksik
-  * opt.h, def.h, others: patch #8423 "arch/perf.h" should be made an optional item
-
-  2015-02-11: Simon Goldschmidt
-  * api_msg.c, opt.h: started to implement fullduplex sockets/netconns
-    (note that this is highly unstable yet!)
-
-  2015-01-17: Simon Goldschmidt
-  * api: allow enabling socket API without (public) netconn API - netconn API is
-    still used by sockets, but keeping it private (static) should allow better
-    compiler optimizations
-
-  2015-01-16: Simon Goldschmidt
-  * tcp_in.c: fixed bug #20506 "Initial congestion window is very small" again
-    by implementing the calculation formula from RFC3390
-
-  2014-12-10: Simon Goldschmidt
-  * api: added option LWIP_NETCONN_SEM_PER_THREAD to use a semaphore per thread
-    instead of using one per netconn and per select call
-
-  2014-12-08: Simon Goldschmidt
-  * ip6.h: fixed bug #43778: IPv6 header version not set on 16-bit platform
-    (macro IP6H_VTCFL_SET())
-
-  2014-12-08: Simon Goldschmidt
-  * icmp.c, ip4.c, pbuf.c, udp.c, pbuf.h: task #11472 Support PBUF_REF for RX
-    (IPv6 and IPv4/v6 reassembly might not work yet)
-
-  2014-11-06: Simon Goldschmidt
-  * sockets.c/.h, init.c: lwip_socket_init() is not needed any more
-    -> compatibility define
-
-  2014-09-16: Simon Goldschmidt
-  * dns.c, opt.h: reduced ram usage by parsing DNS responses in place
-
-  2014-09-16: Simon Goldschmidt
-  * pbuf.h/.c: added pbuf_take_at() and pbuf_put_at()
-
-  2014-09-15: Simon Goldschmidt
-  * dns.c: added source port randomization to make the DNS client more robust
-    (see bug #43144)
-
-  2013-09-02: Simon Goldschmidt
-  * arch.h and many other files: added optional macros PACK_STRUCT_FLD_8() and
-    PACK_STRUCT_FLD_S() to prevent gcc 4 from warning about struct members that
-    do not need packing
-
-  2013-08-19: Simon Goldschmidt
-  * netif.h: bug #42998: made NETIF_MAX_HWADDR_LEN overridable for some special
-    networks
-
-  2013-03-17: Simon Goldschmidt (patch by Ghobad Emadi)
-  * opt.h, etharp.c: Added LWIP_HOOK_ETHARP_GET_GW to implement IPv4 routing with
-    multiple gateways
-
-  2013-04-20: Fatih Asici
-  * opt.h, etharp.h/.c: patch #7993: Added support for transmitting packets
-    with VLAN headers via hook function LWIP_HOOK_VLAN_SET and to check them
-    via hook function LWIP_HOOK_VLAN_CHECK
-
-  2014-02-20: Simon Goldschmidt (based on patch by Artem Pisarenko)
-  * patch #7885: modification of api modules to support FreeRTOS-MPU
-    (don't pass stack-pointers to other threads)
-
-  2014-02-05: Simon Goldschmidt (patch by "xtian" and "alex_ab")
-  * patch #6537/#7858: TCP window scaling support
-
-  2014-01-17: Jiri Engelthaler
-  * icmp, icmp6, opt.h: patch #8027: Completed HW checksuming for IPv4 and
-    IPv6 ICMP's
-
-  2012-08-22: Sylvain Rochet
-  * New PPP stack for lwIP, developed in ppp-new branch.
-    Based from pppd 2.4.5, released 2009-11-17, with huge changes to match
-    code size and memory requirements for embedded devices, including:
-    - Gluing together the previous low-level PPP code in lwIP to pppd 2.4.5, which
-      is more or less what pppd sys-* files are, so that we get something working
-      using the unix port.
-    - Merged some patchs from lwIP Git repository which add interesting features
-      or fix bugs.
-    - Merged some patchs from Debian pppd package which add interesting features
-      or fix bugs.
-    - Ported PPP timeout handling to the lwIP timers system
-    - Disabled all the PPP code using filesystem access, replaced in necessary cases
-      to configuration variables.
-    - Disabled all the PPP code forking processes.
-    - Removed IPX support, lwIP does not support IPX.
-    - Ported and improved random module from the previous PPP port.
-    - Removed samba TDB (file-driven database) usage, because it needs a filesystem.
-    - MS-CHAP required a DES implementation, we added the latest PolarSSL DES
-      implementation which is under a BSD-ish license.
-    - Also switched to PolarSSL MD4,MD5,SHA1 implementations, which are meant to be
-      used in embedded devices with reduced memory footprint.
-    - Removed PPP configuration file parsing support. 
-    - Added macro definition EAP_SUPPORT to make EAP support optional.
-    - Added macro definition CHAP_SUPPORT to make CHAP support optional.
-    - Added macro definition MSCHAP_SUPPORT to make MSCHAP support optional.
-    - Added macro definition PAP_SUPPORT to make PAP support optional.
-    - Cleared all Linux syscall calls.
-    - Disabled demand support using a macro, so that it can be ported later.
-    - Disabled ECP support using a macro, so that it can be ported later.
-    - Disabled CCP support using a macro, so that it can be ported later.
-    - Disabled CBCP support using a macro, so that it can be ported later.
-    - Disabled LQR support using a macro, so that it can be ported later.
-    - Print packet debug feature optional, through PRINTPKT_SUPPORT
-    - Removed POSIX signal usage.
-    - Fully ported PPPoS code from the previous port.
-    - Fully ported PPPoE code from the previous port.
-    - Fully ported VJ compression protocol code from the previous port.
-    - Removed all malloc()/free() use from PPP, replaced by stack usage or PBUF.
-    - Disabled PPP server support using a macro, so that it can be ported later.
-    - Switched all PPP debug to lwIP debug system.
-    - Created PPP Control Block (PPP PCB), removed PPP unit integer everywhere,
-      removed all global variables everywhere, did everything necessary for
-      the PPP stack to support more than one PPP session (pppd only support
-      one session per process).
-    - Removed the statically allocated output buffer, now using PBUF.
-    - Improved structure size of all PPP modules, deep analyze of code to reduce
-      variables size to the bare minimum. Switched all boolean type (char type in
-      most architecture) to compiler generated bitfields.
-    - Added PPP IPv6 support, glued lwIP IPv6 support to PPP.
-    - Now using a persistent netif interface which can then be used in lwIP
-      functions requiring a netif.
-    - Now initializing PPP in lwip_init() function.
-    - Reworked completely the PPP state machine, so that we don't end up in
-      anymore in inconsistent state, especially with PPPoE.
-    - Improved the way we handle PPP reconnection after disconnect, cleaning
-      everything required so that we start the PPP connection again from a
-      clean state.
-    - Added PPP holdoff support, allow the lwIP user to wait a little bit before
-      reconnecting, prevents connection flood, especially when using PPPoL2TP.
-    - Added PPPoL2TP LAC support (a.k.a. UDP tunnels), adding a VPN client
-      feature to lwIP, L2TP being a widely used tunnel protocol.
-    - Switched all used PPP types to lwIP types (u8t, u16t, u32t, ...)
-    - Added PPP API "sequential" thread-safe API, based from NETIFAPI.
-
-  2011-07-21: Simon Goldschmidt
-  * sockets.c, opt.h: (bug #30185): added LWIP_FIONREAD_LINUXMODE that makes
-    ioctl/FIONREAD return the size of the next pending datagram.
-
-  2011-05-25: Simon Goldschmidt
-  * again nearly the whole stack, renamed ip.c to ip4.c, ip_addr.c to ip4_addr.c,
-    combined ipv4/ipv6 inet_chksum.c, added ip.h, ip_addr.h: Combined IPv4
-    and IPv6 code where possible, added defines to access IPv4/IPv6 in non-IP
-    code so that the code is more readable.
-
-  2011-05-17: Patch by Ivan Delamer (only checked in by Simon Goldschmidt)
-  * nearly the whole stack: Finally, we got decent IPv6 support, big thanks to
-    Ivan! (this is work in progress: we're just post release anyway :-)
-
-
-  ++ Bugfixes:
-
-  2016-08-23: Simon Goldschmidt
-  * etharp: removed ETHARP_TRUST_IP_MAC since it is insecure and we don't need
-    it any more after implementing unicast ARP renewal towards arp entry timeout
-
-  2016-07-20: Simon Goldschmidt
-  * memp.h/.c: fixed bug #48442 (memp stats don't work for MEMP_MEM_MALLOC)
-
-  2016-07-21: Simon Goldschmidt (patch by Ambroz Bizjak)
-  * tcp_in.c, tcp_out.c: fixed bug #48543 (TCP sent callback may prematurely
-    report sent data when only part of a segment is acked) and don't include
-    SYN/FIN in snd_buf counter
-
-  2016-07-19: Simon Goldschmidt
-  * etharp.c: fixed bug #48477 (ARP input packet might update static entry)
-
-  2016-07-11: Simon Goldschmidt
-  * tcp_in.c: fixed bug #48476 (TCP sent callback called wrongly due to picking
-    up old pcb->acked
-
-  2016-06-30: Simon Goldschmidt (original patch by Fabian Koch)
-  * tcp_in.c: fixed bug #48170 (Vulnerable to TCP RST spoofing)
-
-  2016-05-20: Dirk Ziegelmeier
-  * sntp.h/.c: Fix return value of sntp_getserver() call to return a pointer
-
-  2016-04-05: Simon Goldschmidt (patch by Philip Gladstone)
-  * udp.c: patch #8358: allow more combinations of listening PCB for IPv6
-
-  2016-04-05: Simon Goldschmidt
-  * netconn/socket API: fixed bug# 43739 (Accept not reporting errors about
-    aborted connections): netconn_accept() returns ERR_ABRT (sockets: ECONNABORTED)
-    for aborted connections, ERR_CLSD (sockets: EINVAL) if the listening netconn
-    is closed, which better seems to follow the standard.
-
-  2016-03-23: Florent Matignon
-  * dhcp.c: fixed bug #38203: DHCP options are not recorded in all DHCP ack messages
-
-  2016-03-22: Simon Goldschmidt
-  * tcp: changed accept handling to be done internally: the application does not
-    have to call tcp_accepted() any more. Instead, when delaying accept (e.g. sockets
-    do), call tcp_backlog_delayed()/tcp_backlog_accepted() (fixes bug #46696)
-
-  2016-03-22: Simon Goldschmidt
-  * dns.c: ignore dns response parsing errors, only abort resolving for correct
-    responses or error responses from correct server (bug #47459)
-
-  2016-03-17: Simon Goldschmidt
-  * api_msg.c: fixed bug #47448 (netconn/socket leak if RST is received during close)
-
-  2016-03-17: Joel Cunningham
-  * api_msg.c: don't fail closing a socket/netconn when failing to allocate the
-    FIN segment; blocking the calling thread for a while is better than risking
-    leaking a netconn/socket (see bug #46701)
-
-  2016-03-16: Joel Cunningham
-  * tcp_out.c: reset rto timer on fast retransmission
-
-  2016-03-16: Deomid Ryabkov
-  * tcp_out.c: fixed bug #46384 Segment size calculation bug with MSS != TCP_MSS
-
-  2016-03-05: Simon Goldschmidt
-  * err.h/.c, sockets.c: ERR_IF is not necessarily a fatal error
-
-  2015-11-19: fix by Kerem Hadimli
-  * sockets.c: fixed bug #46471: lwip_accept() leaks socket descriptors if new
-    netconn was already closed because of peer behavior
-
-  2015-11-12: fix by Valery Ushakov
-  * tcp_in.c: fixed bug #46365 tcp_accept_null() should call tcp_abort()
-
-  2015-10-02: Dirk Ziegelmeier/Simon Goldschmidt
-  * snmp: cleaned up snmp structs API (fixed race conditions from bug #46089,
-    reduce ram/rom usage of tables): incompatible change for private MIBs
-
-  2015-09-30: Simon Goldschmidt
-  * ip4_addr.c: fixed bug #46072: ip4addr_aton() does not check the number range
-    of all address parts
-
-  2015-08-28: Simon Goldschmidt
-  * tcp.c, tcp_in.c: fixed bug #44023: TCP ssthresh value is unclear: ssthresh
-    is set to the full send window for active open, too, and is updated once
-    after SYN to ensure the correct send window is used
-
-  2015-08-28: Simon Goldschmidt
-  * tcp: fixed bug #45559: Window scaling casts u32_t to u16_t without checks
-
-  2015-08-26: Simon Goldschmidt
-  * ip6_frag.h/.c: fixed bug bug #41009: IPv6 reassembly broken on 64-bit platforms:
-    define IPV6_FRAG_COPYHEADER==1 on these platforms to copy the IPv6 header
-    instead of referencing it, which gives more room for struct ip6_reass_helper
-
-  2015-08-25: Simon Goldschmidt
-  * sockets.c: fixed bug #45827: recvfrom: TCP window is updated with MSG_PEEK
-
-  2015-08-20: Manoj Kumar
-  * snmp_msg.h, msg_in.c: fixed bug #43790: Sending octet string of Length >255
-    from SNMP agent
-
-  2015-08-19: Jens Nielsen
-  * icmp.c, ip4.c, tcp_in.c, udp.c, raw.c: fixed bug #45120: Broadcast & multiple
-    interfaces handling
-
-  2015-08-19: Simon Goldschmidt (patch by "Sandra")
-  * dns.c: fixed bug #45004: dns response without answer might be discarded
-
-  2015-08-18: Chrysn
-  * timers.c: patch #8704 fix sys_timeouts_sleeptime function
-
-  2015-07-01: Erik Ekman
-  * puf.c: fixed bug #45454 (pbuf_take_at() skips write and returns OK if offset
-    is at start of pbuf in chain)
-
-  2015-05-19: Simon Goldschmidt
-  * dhcp.h/.c: fixed bugs #45140 and #45141 (dhcp was not stopped correctly after
-    fixing bug #38204)
-
-  2015-03-21: Simon Goldschmidt (patch by Homyak)
-  * tcp_in.c: fixed bug #44766 (LWIP_WND_SCALE: tcphdr->wnd was not scaled in
-    two places)
-
-  2015-03-21: Simon Goldschmidt
-  * tcp_impl.h, tcp.c, tcp_in.c: fixed bug #41318 (Bad memory ref in tcp_input()
-    after tcp_close())
-
-  2015-03-21: Simon Goldschmidt
-  * tcp_in.c: fixed bug #38468 (tcp_sent() not called on half-open connection for
-    data ACKed with the same ack as FIN)
-
-  2015-03-21: Simon Goldschmidt (patch by Christoffer Lind)
-  * dhcp.h/.c: fixed bug #38204 (DHCP lease time not handled correctly)
-
-  2015-03-20: Simon Goldschmidt
-  * dhcp.c: fixed bug #38714 (Missing option and client address in DHCPRELEASE message)
-
-  2015-03-19: Simon Goldschmidt
-  * api.h, tcpip.h, api_lib.c, api_msg.c: fixed race conditions in assigning
-    netconn->last_err (fixed bugs #38121 and #37676)
-
-  2015-03-09: Simon Goldschmidt
-  * ip4.c: fixed the IPv4 part of bug #43904 (ip_route() must detect linkup status)
-
-  2015-03-04: Simon Goldschmidt
-  * nd6.c: fixed bug #43784 (a host should send at least one Router Solicitation)
-
-  2015-03-04: Valery Ushakov
-  * ip6.c: fixed bug #41094 (Byte-order bug in IPv6 fragmentation header test)
-
-  2015-03-04: Zach Smith
-  * nd6.c: fixed bug #38153 (nd6_input() byte order issues)
-
-  2015-02-26: Simon Goldschmidt
-  * netif.c, tcp.h/.c: fixed bug #44378 (TCP connections are not aborted on netif
-    remove)
-
-  2015-02-25: Simon Goldschmidt
-  * ip4.c, etharp.c: fixed bug #40177 (System hangs when dealing with corrupted
-    packets), implemented task #12357 (Ensure that malicious packets don't
-    assert-fail): improved some pbuf_header calls to not assert-fail.
-
-  2015-02-25: patch by Joel Cunningham
-  * udp.h/.c, sockets.c: fixed bug #43028 (IP_MULTICAST_TTL affects unicast
-    datagrams)
-
-  2015-02-25: patch by Greg Renda
-  * ip4_frag.c: fixed bug #38210 (ip reassembly while remove oldest datagram)
-
-  2015-02-25: Simon Goldschmidt
-  * sockets.c: fixed bug #38165 (socket with mulicast): ensure igmp membership
-    are dropped when socket (not netconn!) is closed.
-
-  2015-02-25: Simon Goldschmidt
-  * ip4.h/.c, udp.c: fixed bug #38061 (wrong multicast routing in IPv4) by
-    adding an optional default netif for multicast routing
-
-  2015-02-25: Simon Goldschmidt
-  * netconn API: fixed that netconn_connect still used message passing for
-    LWIP_TCPIP_CORE_LOCKING==1
-
-  2015-02-22: patch by Jens Nielsen
-  * icmp.c: fixed bug #38803 (Source address in broadcast ping reply)
-
-  2015-02-22: Simon Goldschmidt
-  * udp.h, sockets.c: added proper accessor functions for pcb->multicast_ip
-    (previously used by get/setsockopt only)
-
-  2015-02-18: Simon Goldschmidt
-  * sockets.c: Fixed select not reporting received FIN as 'readable' in certain
-    rare cases (bug #43779: select(), close(), and TCP retransmission error)
-
-  2015-02-17: Simon Goldschmidt
-  * err.h, sockets.c, api_msg.c: fixed bug #38853 "connect() use a wrong errno":
-    return ERR_ALREADY/EALRADY during connect, ERR_ISCONN/EISCONN when already
-    connected
-
-  2015-02-17: Simon Goldschmidt
-  * tcp_impl.h, tcp_out.c, tcp.c, api_msg.c: fixed bug #37614 "Errors from
-    ipX_output are not processed". Now tcp_output(_segment) checks for the return
-    value of ipX_output and does not try to send more on error. A netif driver
-    can call tcp_txnow() (from tcpip_thread!) to try to send again if TX buffers
-    are available again.
-
-  2015-02-14: patches by Freddie Chopin
-  * snmp*: made community writable, fixed some const pointers
-
-  2015-02-13: Simon Goldschmidt
-  * msg_in.c: fixed bug #22070 "MIB_OBJECT_WRITE_ONLY not implemented in SNMP"
-
-  2015-02-12: Simon Goldschmidt
-  * ip.h, ip4.c, ip6.c: fixed bug #36403 "ip4_input() and ip6_input() always pass
-    inp to higher layers": now the accepting netif is passed up, but the input
-    netif is available through ip_current_input_netif() if required.
-
-  2015-02-11: patch by hichard
-  * tcpip.c: fixed bug #43094 "The function tcpip_input() forget to handle IPv6"
-
-  2015-02-10: Simon Goldschmidt
-  * netconn API: fixed that netconn_close/netconn_delete still used message passing
-    for LWIP_TCPIP_CORE_LOCKING==1
-
-  2015-02-10: Simon Goldschmidt
-  * netconn/socket api: fixed bug #44225 "closing TCP socket should time out
-    eventually", implemented task #6930 "Implement SO_LINGER": closing TCP sockets
-    times out after 20 seconds or after the configured SND_TIMEOUT or depending
-    on the linger settings.
-
-  2015-01-27: Simon Goldschmidt
-  * api_msg.c: fixed that SHUT_RD followed by SHUT_WR was different to SHUT_RDWR,
-    fixed return value of lwip_netconn_do_close on unconnected netconns
-
-  2015-01-17: Simon Goldschmidt
-  * sockets.c: fixed bug #43361 select() crashes with stale FDs
-
-  2015-01-17: Simon Goldschmidt
-  * sockets.c/.h, memp_std.h: fixed bug #40788 "lwip_setsockopt_internal() crashes"
-    by rewriting set/getsockopt functions to combine checks with the actual code
-    and add more NULL checks; this also fixes that CORE_LOCKING used message
-    passing for set/getsockopt.
-
-  2014-12-19: Simon Goldschmidt
-  * opt.h, dhcp.h/.c: prevent dhcp from starting when netif link is down (only
-    when LWIP_DHCP_CHECK_LINK_UP==1, which is disabled by default for
-    compatibility reasons)
-
-  2014-12-17: Simon Goldschmidt
-  * tcp_out.c: fixed bug #43840 Checksum error for TCP_CHECKSUM_ON_COPY==1 for
-    no-copy data with odd length
-
-  2014-12-10: Simon Goldschmidt
-  * sockets.c, tcp.c, others: fixed bug #43797 set/getsockopt: SO_SNDTIMEO/SO_RCVTIMEO
-    take int as option but should take timeval (LWIP_SO_SNDRCVTIMEO_STANDARD==0 can
-    be used to revert to the old 'winsock' style behaviour)
-    Fixed implementation of SO_ACCEPTCONN to just look at the pcb state
-
-  2014-12-09: Simon Goldschmidt
-  * ip4.c: fixed bug #43596 IGMP queries from 0.0.0.0 are discarded
-
-  2014-10-21: Simon Goldschmidt (patch by Joel Cunningham and Albert Huitsing)
-  * sockts.c: fixed bugs #41495 Possible threading issue in select() and #43278
-    event_callback() handle context switch when calling sys_sem_signal()
-
-  2014-10-21: Simon Goldschmidt
-  * api_msg.c: fixed bug #38219 Assert on TCP netconn_write with sndtimeout set
-
-  2014-09-16: Kevin Cernekee
-  * dns.c: patch #8480 Fix handling of dns_seqno wraparound
-
-  2014-09-16: Simon Goldschmidt
-  * tcp_out.c: fixed bug #43192 tcp_enqueue_flags() should not check TCP_SND_QUEUELEN
-    when sending FIN
-
-  2014-09-03: Simon Goldschmidt
-  * msg_in.c: fixed bug #39355 SNMP Memory Leak in case of error
-
-  2014-09-02: Simon Goldschmidt
-  * err.h/.c, sockets.c, api_msg.c: fixed bug #43110 call getpeername() before
-    listen() will cause a error
-
-  2014-09-02: Simon Goldschmidt
-  * sockets.c: fixed bug #42117 lwip_fcntl does not set errno
-
-  2014-09-02: Simon Goldschmidt
-  * tcp.c: fixed bug #42299 tcp_abort() leaves freed pcb on tcp_bound_pcbs list
-
-  2014-08-20: Simon Goldschmidt
-  * dns.c: fixed bug #42987 lwIP is vulnerable to DNS cache poisoning due to
-    non-randomized TXIDs
-
-  2014-06-03: Simon Goldschmidt
-  * tcp_impl.h, tcp_in.c: fixed bug #37969 SYN packet dropped as short packet in
-    tcp_input function
-
-  2014-05-20: Simon Goldschmidt
-  * tcp_out.c: fixed bug #37184 tcp_write problem for pcbs in the SYN_SENT state
-
-  2014-05-19: Simon Goldschmidt
-  * *.h: Fixed bug #35874 reserved identifier violation (removed leading underscores
-    from header include guards)
-
-  2014-04-08: Simon Goldschmidt
-  * tcp.c: Fixed bug #36167 tcp server crash when client closes (maximum window)
-
-  2014-04-06: Simon Goldschmidt
-  * tcp_in.c: Fixed bug #36210 lwIP does not elicit an empty ACK when received
-    unacceptable ACK
-
-  2014-04-06: Simon Goldschmidt
-  * dhcp.c, ip4.c/.h, ip6.c/.h, udp.c/.h, ip.h: Fixed bug #41787 DHCP Discovery
-    is invalid when an IP is set to thet netif.
-
-  2014-03-14: Simon Goldschmidt
-  * tcp_out.c: Fixed bug #36153 TCP Cheksum error if LWIP_CHECKSUM_ON_COPY=1
-
-  2014-03-11: Simon Goldschmidt (patch by Mason)
-  * opt.h, sockets.c: fixed bug #35928 BSD sockets functions must set errno for
-    POSIX-compliance
-
-  2014-02-27: Simon Goldschmidt
-  * dhcp.c: fixed bug #40303 DHCP xid renewed when sending a DHCPREQUEST
-
-  2014-02-27: Simon Goldschmidt
-  * raw.c: fixed bug #41680 raw socket can not receive IPv6 packet when
-    IP_SOF_BROADCAST_RECV==1
-
-  2014-02-27: Simon Goldschmidt
-  * api_msg.c, sockets.c: fixed bug #38404 getpeeraddr returns success on
-    unconnected/listening TCP sockets
-
-  2014-02-27: Simon Goldschmidt
-  * sockets.c: fixed bug #41729 Some socket functions return Exyz instead of -1
-
-  2014-02-25: Simon Goldschmidt
-  * ip4.c: fixed bug #39514 ip_route() may return an IPv6-only interface
-
-  2014-02-25: Simon Goldschmidt, patch by Fatih Asici
-  * pbuf.c: fixed bug #39356 Wrong increment in pbuf_memfind()
-
-  2014-02-25: Simon Goldschmidt
-  * netif.c/.h, udp.c: fixed bug #39225 udp.c uses netif_matches_ip6_addr() incorrectly;
-    renamed function netif_matches_ip6_addr() to netif_get_ip6_addr_match()
-
-  2014-02-25: Simon Goldschmidt
-  * igmp.c: fixed bug #39145 IGMP membership report for 224.0.0.1
-
-  2014-02-22: Simon Goldschmidt (patch by Amir Shalem)
-  * etharp.c, opt.h: fixed bug #34681 Limit ARP queue length by ARP_QUEUE_LEN (=3)
-
-  2014-02-22: Simon Goldschmidt (patch by Amir Shalem)
-  * etharp.h/.c: fixed bug #34682 Limit ARP request flood for unresolved entry
-
-  2014-02-20: Simon Goldschmidt
-  * tcp_out.c: fixed bug #39683 Assertion "seg->tcphdr not aligned" failed with
-    MEM_ALIGNMENT = 8
-
-  2014-02-20: Simon Goldschmidt
-  * sockets.c: fixed bug #39882 No function shall set errno to 0
-
-  2014-02-20: Simon Goldschmidt
-  * mib_structs.c: fixed bug #40050 SNMP problem with MIB arrays > 255
-
-  2014-02-20: Simon Goldschmidt
-  * api.h, sockets.c: fixed bug #41499 netconn::recv_avail can overflow
-
-  2014-01-08: Stathis Voukelatos
-  * memp_std.h: patch #7928 Fixed size calculation in MALLOC memory pool
-    creation macro
-
-  2014-01-18: Brian Fahs
-  * tcp_out.c: patch #8237: tcp_rexmit_rto fails to update pcb->unsent_oversize
-    when necessary
-
-  2014-01-17: Grant Erickson, Jay Logue, Simon Goldschmidt
-  * ipv6.c, netif.c: patch #7913 Enable Support for IPv6 Loopback
-
-  2014-01-16: Stathis Voukelatos
-  * netif.c: patch #7902 Fixed netif_poll() operation when LWIP_LOOPBACK_MAX_PBUFS > 0
-
-  2014-01-14: "Freddie Chopin"
-  * snmp.h, mib2.c: fixed constness and spelling of sysdescr
-
-  2014-01-14: Simon Goldschmidt (patch by Thomas Faber)
-  * tcpip.c: patch #8241: Fix implicit declaration of ip_input with
-    LWIP_TCPIP_CORE_LOCKING_INPUT disabled
-
-  2014-01-14: chrysn
-  * timers.c: patch #8244 make timeouts usable reliably from outside of the
-    timeout routine
-
-  2014-01-10: Simon Goldschmidt
-  * ip_frag.c, ip6_frag.c: fixed bug #41041 Potential use-after-free in IPv6 reassembly
-
-  2014-01-10: Simon Goldschmidt
-  * memp.c: fixed bug #41188 Alignment error in memp_init() when MEMP_SEPARATE_POOLS==1
-
-  2014-01-10: Simon Goldschmidt
-  * tcp.c: fixed bug #39898 tcp_fasttmr() possible lock due to infinte queue process loop
-
-  2013-06-29: Simon Goldschmidt
-  * inet.h, sockets.h: partially fixed bug #37585: IPv6 compatibility (in socket structs)
-
-  2013-06-29: Simon Goldschmidt
-  * inet6.h: bug #37585/task #12600: fixed struct in6_addr.s6_addr to conform to spec
-
-  2013-04-24: patch by Liam <morepork>
-  * api_msg.c: patch #8008 Fix a potential null pointer dereference in assert
-
-  2013-04-24: Simon Goldschmidt
-  * igmp.c: fixed possible division by zero
-
-  2013-04-24: Simon Goldschmidt
-  * ip6.h, some ipv6 C files: fixed bug #38526 Coverity: Recursive Header Inclusion in ip6.h
-
-  2013-04-24: Simon Goldschmidt (patch by Emil Ljungdahl):
-  * netif.c: fixed bug #38586 netif_loop_output() "deadlocks"
-
-  2013-01-15: Simon Goldschmidt
-  * ip4.c: fixed bug #37665 ip_canforward operates on address in wrong byte order
-
-  2013-01-15: Simon Goldschmidt
-  * pbuf.h: fixed bug #38097 pbuf_free_ooseq() warning
-
-  2013-01-14: Simon Goldschmidt
-  * dns.c: fixed bug #37705 Possible memory corruption in DNS query
-
-  2013-01-11: Simon Goldschmidt
-  * raw.c: fixed bug #38066 Raw pcbs can alter packet without eating it
-
-  2012-08-22: Simon Goldschmidt
-  * memp.c: fixed bug #37166: memp_sanity check loops itself
-
-  2012-08-13: Simon Goldschmidt
-  * dhcp.c: fixed bug #36645: Calling dhcp_release before dhcp_start
-    dereferences NULL
-
-  2012-08-13: Simon Goldschmidt
-  * msg_out.c: fixed bug #36840 snmp_send_trap() NULL de-reference if traps
-    configured but no interfaces available
-
-  2012-08-13: Simon Goldschmidt
-  * dns.c: fixed bug #36899 DNS TTL 0 is cached for a long time
-
-  2012-05-11: Simon Goldschmidt (patch by Marty)
-  * memp.c: fixed bug #36412: memp.c does not compile when
-    MEMP_OVERFLOW_CHECK > zero and MEMP_SEPARATE_POOLS == 1
-
-  2012-05-03: Simon Goldschmidt (patch by Sylvain Rochet)
-  * ppp.c: fixed bug #36283 (PPP struct used on header size computation and
-    not packed)
-
-  2012-05-03: Simon Goldschmidt (patch by David Empson)
-  * ppp.c: fixed bug #36388 (PPP: checksum-only in last pbuf leads to pbuf with
-    zero length)
-
-  2012-03-25: Simon Goldschmidt
-  * api_msg.c: Fixed bug #35817: do_connect() invalidly signals op_completed
-    for UDP/RAW with LWIP_TCPIP_CORE_LOCKING==1
-
-  2012-03-25: Simon Goldschmidt
-  * api_msg.h, api_lib.c, api_msg.c, netifapi.c: fixed bug #35931: Name space
-    pollution in api_msg.c and netifapi.c
-
-  2011-08-24: Simon Goldschmidt
-  * inet6.h: fixed bug #34124 struct in6_addr does not conform to the standard
-
-
-
-(STABLE-1.4.1)
-
-  ++ New features:
-
-  2012-03-25: Simon Goldschmidt (idea by Mason)
-  * posix/*: added posix-compatibility include files posix/netdb.h and posix/sys/socket.h
-    which are a simple wrapper to the correct lwIP include files.
- 
-  2012-01-16: Simon Goldschmidt
-  * opt.h, icmp.c: Added option CHECKSUM_GEN_ICMP
-
-  2011-12-17: Simon Goldschmidt
-  * ip.h: implemented API functions to access so_options of IP pcbs (UDP, TCP, RAW)
-    (fixes bug #35061)
-
-  2011-09-27: Simon Goldschmidt
-  * opt.h, tcp.c, tcp_in.c: Implemented limiting data on ooseq queue (task #9989)
-    (define TCP_OOSEQ_MAX_BYTES / TCP_OOSEQ_MAX_PBUFS in lwipopts.h)
-
-  2011-09-21: Simon Goldschmidt
-  * opt.h, api.h, api_lib.c, api_msg.h/.c, sockets.c: Implemented timeout on
-    send (TCP only, bug #33820)
-
-  2011-09-21: Simon Goldschmidt
-  * init.c: Converted runtime-sanity-checks into compile-time checks that can
-    be disabled (since runtime checks can often not be seen on embedded targets)
-
-  2011-09-11: Simon Goldschmidt
-  * ppp.h, ppp_impl.h: splitted ppp.h to an internal and external header file
-    to get a clear separation of which functions an application or port may use
-    (task #11281)
-
- 2011-09-11: Simon Goldschmidt
-  * opt.h, tcp_impl.h, tcp.c, udp.h/.c: Added a config option to randomize
-    initial local TCP/UDP ports (so that different port ranges are used after
-    a reboot; bug #33818; this one added tcp_init/udp_init functions again)
-
-  2011-09-03: Simon Goldschmidt
-  * dhcp.c: DHCP uses LWIP_RAND() for xid's (bug #30302)
-
-  2011-08-24: Simon Goldschmidt
-  * opt.h, netif.h/.c: added netif remove callback (bug #32397)
-
-  2011-07-26: Simon Goldschmidt
-  * etharp.c: ETHARP_SUPPORT_VLAN: add support for an external VLAN filter
-    function instead of only checking for one VLAN (define ETHARP_VLAN_CHECK_FN)
-
-  2011-07-21: Simon Goldschmidt (patch by hanhui)
-  * ip4.c, etharp.c, pbuf.h: bug #33634 ip_forward() have a faulty behaviour:
-    Added pbuf flags to mark incoming packets as link-layer broadcast/multicast.
-    Also added code to allow ip_forward() to forward non-broadcast packets to
-    the input netif (set IP_FORWARD_ALLOW_TX_ON_RX_NETIF==1).
-
-  2011-06-26: Simon Goldschmidt (patch by Cameron Gutman)
-  * tcp.c, tcp_out.c: bug #33604: added some more asserts to check that
-    pcb->state != LISTEN
-
-   2011-05-14: Simon Goldschmidt (patch by Stéphane Lesage)
-  * tcpip.c/.h: patch #7449 allow tcpip callback from interrupt with static
-    memory message
-
-
-  ++ Bugfixes:
-
-  2012-09-26: Simon Goldschmidt
-  * api_msg.c: fixed bug #37405 'err_tcp()' uses already freed 'netconn' object
-
-  2012-09-26: patch by Henrik Persson
-  * dhcp.c: patch #7843 Fix corner case with dhcp timeouts
-
-  2012-09-26: patch by Henrik Persson
-  * dhcp.c: patch #7840 Segfault in dhcp_parse_reply if no end marker in dhcp packet
-
-  2012-08-22: Simon Goldschmidt
-  * memp.c: fixed bug #37166: memp_sanity check loops itself
-
-  2012-05-08: Simon Goldschmidt
-  * tcp_out.c: fixed bug: #36380 unsent_oversize mismatch in 1.4.1RC1 (this was
-    a debug-check issue only)
-
-  2012-03-27: Simon Goldschmidt
-  * vj.c: fixed bug #35756 header length calculation problem in ppp/vj.c
-
-  2012-03-27: Simon Goldschmidt (patch by Mason)
-  * tcp_out.c: fixed bug #35945: SYN packet should provide the recv MSS not the
-    send MSS
-
-  2012-03-22: Simon Goldschmidt
-  * ip4.c: fixed bug #35927: missing refragmentaion in ip_forward
- 
-  2012-03-20: Simon Goldschmidt (patch by Mason)
-  * netdb.c: fixed bug #35907: lwip_gethostbyname_r returns an invalid h_addr_list
- 
-  2012-03-12: Simon Goldschmidt (patch by Bostjan Meglic)
-  * ppp.c: fixed bug #35809: PPP GetMask(): Compiler warning on big endian,
-    possible bug on little endian system
-
-  2012-02-23: Simon Goldschmidt
-  * etharp.c: fixed bug #35595: Impossible to send broadcast without a gateway
-    (introduced when fixing bug# 33551)
-
-  2012-02-16: Simon Goldschmidt
-  * ppp.c: fixed pbuf leak when PPP session is aborted through pppSigHUP()
-    (bug #35541: PPP Memory Leak)
-
-  2012-02-16: Simon Goldschmidt
-  * etharp.c: fixed bug #35531: Impossible to send multicast without a gateway
-    (introduced when fixing bug# 33551)
-
-  2012-02-16: Simon Goldschmidt (patch by Stéphane Lesage)
-  * msg_in.c, msg_out.c: fixed bug #35536 SNMP: error too big response is malformed
-
-  2012-02-15: Simon Goldschmidt
-  * init.c: fixed bug #35537: MEMP_NUM_* sanity checks should be disabled with
-    MEMP_MEM_MALLOC==1
-
-  2012-02-12: Simon Goldschmidt
-  * tcp.h, tcp_in.c, tcp_out.c: partly fixed bug #25882: TCP hangs on
-    MSS > pcb->snd_wnd (by not creating segments bigger than half the window)
-
-  2012-02-11: Simon Goldschmidt
-  * tcp.c: fixed bug #35435: No pcb state check before adding it to time-wait
-    queue while closing
-
-  2012-01-22: Simon Goldschmidt
-  * tcp.c, tcp_in.c: fixed bug #35305: pcb may be freed too early on shutdown(WR)
-
-  2012-01-21: Simon Goldschmidt
-  * tcp.c: fixed bug #34636: FIN_WAIT_2 - Incorrect shutdown of TCP pcb
-
-  2012-01-20: Simon Goldschmidt
-  * dhcp.c: fixed bug #35151: DHCP asserts on incoming option lengths
-
- 2012-01-20: Simon Goldschmidt
-  * pbuf.c: fixed bug #35291: NULL pointer in pbuf_copy
-
-  2011-11-25: Simon Goldschmidt
-  * tcp.h/.c, tcp_impl.h, tcp_in.c: fixed bug #31177: tcp timers can corrupt
-    tcp_active_pcbs in some cases
-
-  2011-11-23: Simon Goldschmidt
-  * sys.c: fixed bug #34884: sys_msleep() body needs to be surrounded with
-    '#ifndef sys_msleep'
-
-  2011-11-22: Simon Goldschmidt
-  * netif.c, etharp.h/.c: fixed bug #34684: Clear the arp table cache when
-    netif is brought down
-
-  2011-10-28: Simon Goldschmidt
-  * tcp_in.c: fixed bug #34638: Dead code in tcp_receive - pcb->dupacks
-
-  2011-10-23: Simon Goldschmidt
-  * mem.c: fixed bug #34429: possible memory corruption with
-    LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT set to 1
-
-  2011-10-18: Simon Goldschmidt
-  * arch.h, netdb.c: fixed bug #34592: lwip_gethostbyname_r uses nonstandard
-    error value
-
-  2011-10-18: Simon Goldschmidt
-  * opt.h: fixed default values of TCP_SNDLOWAT and TCP_SNDQUEUELOWAT for small
-    windows (bug #34176 select after non-blocking send times out)
-
-  2011-10-18: Simon Goldschmidt
-  * tcp_impl.h, tcp_out.c: fixed bug #34587: TCP_BUILD_MSS_OPTION doesn't
-    consider netif->mtu, causes slow network
-
-  2011-10-18: Simon Goldschmidt
-  * sockets.c: fixed bug #34581 missing parentheses in udplite sockets code
-
-  2011-10-18: Simon Goldschmidt
-  * sockets.h: fixed bug #34580 fcntl() is missing in LWIP_COMPAT_SOCKETS
-
-  2011-10-17: Simon Goldschmidt
-  * api_msg.c: fixed bug #34569: shutdown(SHUT_WR) crashes netconn/socket api
-
-  2011-10-13: Simon Goldschmidt
-  * tcp_in.c, tcp_out.c: fixed bug #34517 (persist timer is started although no
-    zero window is received) by starting the persist timer when a zero window is
-    received, not when we have more data queued for sending than fits into the
-    window
-
-  2011-10-13: Simon Goldschmidt
-  * def.h, timers.c: fixed bug #34541: LWIP_U32_DIFF is unnecessarily complex
-
-  2011-10-13: Simon Goldschmidt
-  * sockets.c, api_lib.c: fixed bug #34540: compiler error when CORE_LOCKING is
-    used and not all protocols are enabled
-
-  2011-10-12: Simon Goldschmidt
-  * pbuf.c: fixed bug #34534: Error in sending fragmented IP if MEM_ALIGNMENT > 4
-
-  2011-10-09: Simon Goldschmidt
-  * tcp_out.c: fixed bug #34426: tcp_zero_window_probe() transmits incorrect
-    byte value when pcb->unacked != NULL
-
-  2011-10-09: Simon Goldschmidt
-  * ip4.c: fixed bug #34447 LWIP_IP_ACCEPT_UDP_PORT(dst_port) wrong
-
-  2011-09-27: Simon Goldschmidt
-  * tcp_in.c, tcp_out.c: Reset pcb->unsent_oversize in 2 more places...
-
-  2011-09-27: Simon Goldschmidt
-  * tcp_in.c: fixed bug #28288: Data after FIN in oos queue
-
-  2011-09-27: Simon Goldschmidt
-  * dhcp.c: fixed bug #34406 dhcp_option_hostname() can overflow the pbuf
-
-  2011-09-24: Simon Goldschmidt
-  * mem.h: fixed bug #34377 MEM_SIZE_F is not defined if MEM_LIBC_MALLOC==1
-
-  2011-09-23: Simon Goldschmidt
-  * pbuf.h, tcp.c, tcp_in.c: fixed bug #33871: rejecting TCP_EVENT_RECV() for
-    the last packet including FIN can lose data
-
-  2011-09-22: Simon Goldschmidt
-  * tcp_impl.h: fixed bug #34355: nagle does not take snd_buf/snd_queuelen into
-    account
-
-  2011-09-21: Simon Goldschmidt
-  * opt.h: fixed default value of TCP_SND_BUF to not violate the sanity checks
-    in init.c
-
-  2011-09-20: Simon Goldschmidt
-  * timers.c: fixed bug #34337 (possible NULL pointer in sys_check_timeouts)
-
-  2011-09-11: Simon Goldschmidt
-  * tcp_out.c: use pcb->mss instead of TCP_MSS for preallocate mss-sized pbufs
-    (bug #34019)
-
-  2011-09-09: Simon Goldschmidt
-  * udp.c: fixed bug #34072: UDP broadcast is received from wrong UDP pcb if
-    udp port matches
-
-  2011-09-03: Simon Goldschmidt
-  * tcp_in.c: fixed bug #33952 PUSH flag in incoming packet is lost when packet
-    is aggregated and sent to application
-
-  2011-09-01: Simon Goldschmidt
-  * opt.h: fixed bug #31809 LWIP_EVENT_API in opts.h is inconsistent compared
-    to other options
-
-  2011-09-01: Simon Goldschmidt
-  * tcp_in.c: fixed bug #34111 RST for ACK to listening pcb has wrong seqno
-
-  2011-08-24: Simon Goldschmidt
-  * api_msg.c, sockets.c: fixed bug #33956 Wrong error returned when calling
-    accept() on UDP connections
-
-  2011-08-24: Simon Goldschmidt
-  * sockets.h: fixed bug #34057 socklen_t should be a typedef
-
-  2011-08-24: Simon Goldschmidt
-  * pbuf.c: fixed bug #34112 Odd check in pbuf_alloced_custom (typo)
-
-  2011-08-24: Simon Goldschmidt
-  * dhcp.c: fixed bug #34122 dhcp: hostname can overflow
-
-  2011-08-24: Simon Goldschmidt
-  * netif.c: fixed bug #34121 netif_add/netif_set_ipaddr fail on NULL ipaddr
-
-  2011-08-22: Simon Goldschmidt
-  * tcp_out.c: fixed bug #33962 TF_FIN not always set after FIN is sent. (This
-    merely prevents nagle from not transmitting fast after closing.)
-
-  2011-07-22: Simon Goldschmidt
-  * api_lib.c, api_msg.c, sockets.c, api.h: fixed bug #31084 (socket API returns
-    always EMSGSIZE on non-blocking sockets if data size > send buffers) -> now
-    lwip_send() sends as much as possible for non-blocking sockets
-
-  2011-07-22: Simon Goldschmidt
-  * pbuf.c/.h, timers.c: freeing ooseq pbufs when the pbuf pool is empty implemented
-    for NO_SYS==1: when not using sys_check_timeouts(), call PBUF_CHECK_FREE_OOSEQ()
-    at regular intervals from main level.
-
-  2011-07-21: Simon Goldschmidt
-  * etharp.c: fixed bug #33551 (ARP entries may time out although in use) by
-    sending an ARP request when an ARP entry is used in the last minute before
-    it would time out.
-
-  2011-07-04: Simon Goldschmidt
-  * sys_arch.txt: Fixed documentation after changing sys arch prototypes for 1.4.0.
-
-  2011-06-26: Simon Goldschmidt
-  * tcp.c: fixed bug #31723 (tcp_kill_prio() kills pcbs with the same prio) by
-    updating its documentation only.
-
- 2011-06-26: Simon Goldschmidt
-  * mem.c: fixed bug #33545: With MEM_USE_POOLS==1, mem_malloc can return an
-    unaligned pointer.
-
-  2011-06-26: Simon Goldschmidt
-  * mem.c: fixed bug #33544 "warning in mem.c in lwip 1.4.0 with NO_SYS=1"
-
-   2011-05-25: Simon Goldschmidt
-  * tcp.c: fixed bug #33398 (pointless conversion when checking TCP port range)
-
-
-
-(STABLE-1.4.0)
-
-  ++ New features:
-
-  2011-03-27: Simon Goldschmidt
-  * tcp_impl.h, tcp_in.c, tcp_out.c: Removed 'dataptr' from 'struct tcp_seg' and
-    calculate it in tcp_zero_window_probe (the only place where it was used).
-
-  2010-11-21: Simon Goldschmidt
-  * dhcp.c/.h: Added a function to deallocate the struct dhcp from a netif
-    (fixes bug #31525).
-
-  2010-07-12: Simon Goldschmidt (patch by Stephane Lesage)
-  * ip.c, udp.c/.h, pbuf.h, sockets.c: task #10495: Added support for
-    IP_MULTICAST_LOOP at socket- and raw-API level.
-
-  2010-06-16: Simon Goldschmidt
-  * ip.c: Added an optional define (LWIP_IP_ACCEPT_UDP_PORT) that can allow
-    link-layer-addressed UDP traffic to be received while a netif is down (just
-    like DHCP during configuration)
-
-  2010-05-22: Simon Goldschmidt
-  * many many files: bug #27352: removed packing from ip_addr_t, the packed
-    version is now only used in protocol headers. Added global storage for
-    current src/dest IP address while in input functions.
-
-  2010-05-16: Simon Goldschmidt
-  * def.h: task #10391: Add preprocessor-macros for compile-time htonl
-    calculation (and use them throughout the stack where applicable)
-
-  2010-05-16: Simon Goldschmidt
-  * opt.h, memp_std.h, memp.c, ppp_oe.h/.c: PPPoE now uses its own MEMP pool
-    instead of the heap (moved struct pppoe_softc from ppp_oe.c to ppp_oe.h)
-
-  2010-05-16: Simon Goldschmidt
-  * opt.h, memp_std.h, dns.h/.c: DNS_LOCAL_HOSTLIST_IS_DYNAMIC uses its own
-    MEMP pool instead of the heap
-
-  2010-05-13: Simon Goldschmidt
-  * tcp.c, udp.c: task #6995: Implement SO_REUSEADDR (correctly), added
-    new option SO_REUSE_RXTOALL to pass received UDP broadcast/multicast
-    packets to more than one pcb.
-
-  2010-05-02: Simon Goldschmidt
-  * netbuf.h/.c, sockets.c, api_msg.c: use checksum-on-copy for sending
-    UDP data for LWIP_NETIF_TX_SINGLE_PBUF==1
-
-  2010-04-30: Simon Goldschmidt
-  * udp.h/.c, pbuf.h/.c: task #6849: added udp_send(_to/_if) functions that
-    take a precalculated checksum, added pbuf_fill_chksum() to copy data
-    into a pbuf and at the same time calculating the checksum for that data
-
-  2010-04-29: Simon Goldschmidt
-  * ip_addr.h, etharp.h/.c, autoip.c: Create overridable macros for copying
-    2-byte-aligned IP addresses and MAC addresses
-
-  2010-04-28: Patch by Bill Auerbach
-  * ip.c: Inline generating IP checksum to save a function call
-
-  2010-04-14: Simon Goldschmidt
-  * tcpip.h/.c, timers.c: Added an overridable define to get informed when the
-    tcpip_thread processes messages or timeouts to implement a watchdog.
-
-  2010-03-28: Simon Goldschmidt
-  * ip_frag.c: create a new (contiguous) PBUF_RAM for every outgoing
-    fragment if LWIP_NETIF_TX_SINGLE_PBUF==1
-
-  2010-03-27: Simon Goldschmidt
-  * etharp.c: Speedup TX by moving code from find_entry to etharp_output/
-    etharp_query to prevent unnecessary function calls (inspired by
-    patch #7135).
-
-  2010-03-20: Simon Goldschmidt
-  * opt.h, tcpip.c/.h: Added an option to disable tcpip_(un)timeout code
-    since the linker cannot do this automatically to save space.
-
-  2010-03-20: Simon Goldschmidt
-  * opt.h, etharp.c/.h: Added support for static ARP table entries
-
-  2010-03-14: Simon Goldschmidt
-  * tcp_impl.h, tcp_out.c, inet_chksum.h/.c: task #6849: Calculate checksum
-    when creating TCP segments, not when (re-)transmitting them.
-
-  2010-03-07: Simon Goldschmidt
-  * sockets.c: bug #28775 (select/event_callback: only check select_cb_list
-    on change) plus use SYS_LIGHTWEIGHT_PROT to protect the select code.
-    This should speed up receiving data on sockets as the select code in
-    event_callback is only executed when select is waiting.
-
-  2010-03-06: Simon Goldschmidt
-  * tcp_out.c: task #7013 (Create option to have all packets delivered to
-    netif->output in one piece): Always copy to try to create single pbufs
-    in tcp_write.
-
-  2010-03-06: Simon Goldschmidt
-  * api.h, api_lib.c, sockets.c: task #10167 (sockets: speed up TCP recv
-    by not allocating a netbuf): added function netconn_recv_tcp_pbuf()
-    for tcp netconns to receive pbufs, not netbufs; use that function
-    for tcp sockets.
-
-  2010-03-05: Jakob Ole Stoklundsen / Simon Goldschmidt
-  * opt.h, tcp.h, tcp_impl.h, tcp.c, tcp_in.c, tcp_out.c: task #7040:
-    Work on tcp_enqueue: Don't waste memory when chaining segments,
-    added option TCP_OVERSIZE to prevent creating many small pbufs when
-    calling tcp_write with many small blocks of data. Instead, pbufs are
-    allocated larger than needed and the space is used for later calls to
-    tcp_write.
-
-  2010-02-21: Simon Goldschmidt
-  * stats.c/.h: Added const char* name to mem- and memp-stats for easier
-    debugging.
-
-  2010-02-21: Simon Goldschmidt
-  * tcp.h (and usages), added tcp_impl.h: Splitted API and internal
-    implementation of tcp to make API usage cleare to application programmers
-
-  2010-02-14: Simon Goldschmidt/Stephane Lesage
-  * ip_addr.h: Improved some defines working on ip addresses, added faster
-    macro to copy addresses that cannot be NULL
-
-  2010-02-13: Simon Goldschmidt
-  * api.h, api_lib.c, api_msg.c, sockets.c: task #7865 (implement non-
-    blocking send operation)
-
-  2010-02-12: Simon Goldschmidt
-  * sockets.c/.h: Added a minimal version of posix fctl() to have a
-    standardised way to set O_NONBLOCK for nonblocking sockets.
-
-  2010-02-12: Simon Goldschmidt
-  * dhcp.c/.h, autoip.c/.h: task #10139 (Prefer statically allocated
-    memory): added autoip_set_struct() and dhcp_set_struct() to let autoip
-    and dhcp work with user-allocated structs instead of callin mem_malloc
-
-  2010-02-12: Simon Goldschmidt/Jeff Barber
-  * tcp.c/h: patch #6865 (SO_REUSEADDR for TCP): if pcb.so_options has
-    SOF_REUSEADDR set, allow binding to endpoint in TIME_WAIT
-
-  2010-02-12: Simon Goldschmidt
-  * sys layer: task #10139 (Prefer statically allocated memory): converted
-    mbox and semaphore functions to take pointers to sys_mbox_t/sys_sem_t;
-    converted sys_mbox_new/sys_sem_new to take pointers and return err_t;
-    task #7212: Add Mutex concept in sys_arch (define LWIP_COMPAT_MUTEX
-    to let sys.h use binary semaphores instead of mutexes - as before)
-
-  2010-02-09: Simon Goldschmidt (Simon Kallweit)
-  * timers.c/.h: Added function sys_restart_timeouts() from patch #7085
-    (Restart system timeout handling)
-
-  2010-02-09: Simon Goldschmidt
-  * netif.c/.h, removed loopif.c/.h: task #10153 (Integrate loopif into
-    netif.c) - loopif does not have to be created by the port any more,
-    just define LWIP_HAVE_LOOPIF to 1.
-
-  2010-02-08: Simon Goldschmidt
-  * inet.h, ip_addr.c/.h: Added reentrant versions of inet_ntoa/ipaddr_ntoa
-    inet_ntoa_r/ipaddr_ntoa_r
-
-  2010-02-08: Simon Goldschmidt
-  * netif.h: Added netif_s/get_igmp_mac_filter() macros
-
-  2010-02-05: Simon Goldschmidt
-  * netif.h: Added function-like macros to get/set the hostname on a netif
-
-  2010-02-04: Simon Goldschmidt
-  * nearly every file: Replaced struct ip_addr by typedef ip_addr_t to
-    make changing the actual implementation behind the typedef easier.
-
-  2010-02-01: Simon Goldschmidt
-  * opt.h, memp_std.h, dns.h, netdb.c, memp.c: Let netdb use a memp pool
-    for allocating memory when getaddrinfo() is called.
-
-  2010-01-31: Simon Goldschmidt
-  * dhcp.h, dhcp.c: Reworked the code that parses DHCP options: parse
-    them once instead of parsing for every option. This also removes
-    the need for mem_malloc from dhcp_recv and makes it possible to
-    correctly retrieve the BOOTP file.
-
-  2010-01-30: simon Goldschmidt
-  * sockets.c: Use SYS_LIGHTWEIGHT_PROT instead of a semaphore to protect
-    the sockets array.
-
-  2010-01-29: Simon Goldschmidt (patch by Laura Garrett)
-  * api.h, api_msg.c, sockets.c: Added except set support in select
-    (patch #6860)
-
-  2010-01-29: Simon Goldschmidt (patch by Laura Garrett)
-  * api.h, sockets.h, err.h, api_lib.c, api_msg.c, sockets.c, err.c:
-    Add non-blocking support for connect (partly from patch #6860),
-    plus many cleanups in socket & netconn API.
-
-  2010-01-27: Simon Goldschmidt
-  * opt.h, tcp.h, init.c, api_msg.c: Added TCP_SNDQUEUELOWAT corresponding
-    to TCP_SNDLOWAT and added tcp_sndqueuelen() - this fixes bug #28605
-
-  2010-01-26: Simon Goldschmidt
-  * snmp: Use memp pools for snmp instead of the heap; added 4 new pools.
-
-  2010-01-14: Simon Goldschmidt
-  * ppp.c/.h: Fixed bug #27856: PPP: Set netif link- and status-callback
-    by adding ppp_set_netif_statuscallback()/ppp_set_netif_linkcallback()
-
-  2010-01-13: Simon Goldschmidt
-  * mem.c: The heap now may be moved to user-defined memory by defining
-    LWIP_RAM_HEAP_POINTER as a void pointer to that memory's address
-    (patch #6966 and bug #26133)
-
-  2010-01-10: Simon Goldschmidt (Bill Auerbach)
-  * opt.h, memp.c: patch #6822 (Add option to place memory pools in
-    separate arrays)
-
-  2010-01-10: Simon Goldschmidt
-  * init.c, igmp.c: patch #6463 (IGMP - Adding Random Delay): added define
-    LWIP_RAND() for lwip-wide randomization (to be defined in cc.h)
-
-  2009-12-31: Simon Goldschmidt
-  * tcpip.c, init.c, memp.c, sys.c, memp_std.h, sys.h, tcpip.h
-    added timers.c/.h: Separated timer implementation from semaphore/mbox
-    implementation, moved timer implementation to timers.c/.h, timers are
-    now only called from tcpip_thread or by explicitly checking them.
-    (TASK#7235)
-
-  2009-12-27: Simon Goldschmidt
-  * opt.h, etharp.h/.c, init.c, tcpip.c: Added an additional option
-    LWIP_ETHERNET to support ethernet without ARP (necessary for pure PPPoE)
-
-
-  ++ Bugfixes:
-
-  2011-04-20: Simon Goldschmidt
-  * sys_arch.txt: sys_arch_timeouts() is not needed any more.
-
-  2011-04-13: Simon Goldschmidt
-  * tcp.c, udp.c: Fixed bug #33048 (Bad range for IP source port numbers) by
-    using ports in the IANA private/dynamic range (49152 through 65535).
-
-  2011-03-29: Simon Goldschmidt, patch by Emil Lhungdahl:
-  * etharp.h/.c: Fixed broken VLAN support.
-
-  2011-03-27: Simon Goldschmidt
-  * tcp.c: Fixed bug #32926 (TCP_RMV(&tcp_bound_pcbs) is called on unbound tcp
-    pcbs) by checking if the pcb was bound (local_port != 0).
-
-  2011-03-27: Simon Goldschmidt
-  * ppp.c: Fixed bug #32280 (ppp: a pbuf is freed twice)
-
-  2011-03-27: Simon Goldschmidt
-  * sockets.c: Fixed bug #32906: lwip_connect+lwip_send did not work for udp and
-    raw pcbs with LWIP_TCPIP_CORE_LOCKING==1.
-  
-  2011-03-27: Simon Goldschmidt
-  * tcp_out.c: Fixed bug #32820 (Outgoing TCP connections created before route
-    is present never times out) by starting retransmission timer before checking
-    route.
-
-  2011-03-22: Simon Goldschmidt
-  * ppp.c: Fixed bug #32648 (PPP code crashes when terminating a link) by only
-    calling sio_read_abort() if the file descriptor is valid.
-
-  2011-03-14: Simon Goldschmidt
-  * err.h/.c, sockets.c, api_msg.c: fixed bug #31748 (Calling non-blocking connect
-    more than once can render a socket useless) since it mainly involves changing
-    "FATAL" classification of error codes: ERR_USE and ERR_ISCONN just aren't fatal.
-
-  2011-03-13: Simon Goldschmidt
-  * sockets.c: fixed bug #32769 (ESHUTDOWN is linux-specific) by fixing
-    err_to_errno_table (ERR_CLSD: ENOTCONN instead of ESHUTDOWN), ERR_ISCONN:
-    use EALRADY instead of -1
-
-  2011-03-13: Simon Goldschmidt
-  * api_lib.c: netconn_accept: return ERR_ABRT instead of ERR_CLSD if the
-    connection has been aborted by err_tcp (since this is not a normal closing
-    procedure).
-
-  2011-03-13: Simon Goldschmidt
-  * tcp.c: tcp_bind: return ERR_VAL instead of ERR_ISCONN when trying to bind
-    with pcb->state != CLOSED
-
-  2011-02-17: Simon Goldschmidt
-  * rawapi.txt: Fixed bug #32561 tcp_poll argument definition out-of-order in
-    documentation
-
-  2011-02-17: Simon Goldschmidt
-  * many files: Added missing U/UL modifiers to fix 16-bit-arch portability.
-
-  2011-01-24: Simon Goldschmidt
-  * sockets.c: Fixed bug #31741: lwip_select seems to have threading problems
-
-  2010-12-02: Simon Goldschmidt
-  * err.h: Fixed ERR_IS_FATAL so that ERR_WOULDBLOCK is not fatal.
-
-  2010-11-23: Simon Goldschmidt
-  * api.h, api_lib.c, api_msg.c, sockets.c: netconn.recv_avail is only used for
-    LWIP_SO_RCVBUF and ioctl/FIONREAD.
-
-  2010-11-23: Simon Goldschmidt
-  * etharp.c: Fixed bug #31720: ARP-queueing: RFC 1122 recommends to queue at
-    least 1 packet -> ARP_QUEUEING==0 now queues the most recent packet.
-
-  2010-11-23: Simon Goldschmidt
-  * tcp_in.c: Fixed bug #30577: tcp_input: don't discard ACK-only packets after
-    refusing 'refused_data' again.
-  
-  2010-11-22: Simon Goldschmidt
-  * sockets.c: Fixed bug #31590: getsockopt(... SO_ERROR ...) gives EINPROGRESS
-    after a successful nonblocking connection.
-
-  2010-11-22: Simon Goldschmidt
-  * etharp.c: Fixed bug #31722: IP packets sent with an AutoIP source addr
-    must be sent link-local
-
-  2010-11-22: Simon Goldschmidt
-  * timers.c: patch #7329: tcp_timer_needed prototype was ifdef'ed out for
-    LWIP_TIMERS==0
-
-  2010-11-20: Simon Goldschmidt
-  * sockets.c: Fixed bug #31170: lwip_setsockopt() does not set socket number
-
-  2010-11-20: Simon Goldschmidt
-  * sockets.h: Fixed bug #31304: Changed SHUT_RD, SHUT_WR and SHUT_RDWR to
-    resemble other stacks.
-
-  2010-11-20: Simon Goldschmidt
-  * dns.c: Fixed bug #31535: TCP_SND_QUEUELEN must be at least 2 or else
-    no-copy TCP writes will never succeed.
-
-  2010-11-20: Simon Goldschmidt
-  * dns.c: Fixed bug #31701: Error return value from dns_gethostbyname() does
-    not match documentation: return ERR_ARG instead of ERR_VAL if not
-    initialized or wrong argument.
-
-  2010-10-20: Simon Goldschmidt
-  * sockets.h: Fixed bug #31385: sizeof(struct sockaddr) is 30 but should be 16
-
-  2010-10-05: Simon Goldschmidt
-  * dhcp.c: Once again fixed #30038: DHCP/AutoIP cooperation failed when
-    replugging the network cable after an AutoIP address was assigned.
-
-  2010-08-10: Simon Goldschmidt
-  * tcp.c: Fixed bug #30728: tcp_new_port() did not check listen pcbs
-
-  2010-08-03: Simon Goldschmidt
-  * udp.c, raw.c: Don't chain empty pbufs when sending them (fixes bug #30625)
-
-  2010-08-01: Simon Goldschmidt (patch by Greg Renda)
-  * ppp.c: Applied patch #7264 (PPP protocols are rejected incorrectly on big
-    endian architectures)
-  
-  2010-07-28: Simon Goldschmidt
-  * api_lib.c, api_msg.c, sockets.c, mib2.c: Fixed compilation with TCP or UDP
-    disabled.
-  
-  2010-07-27: Simon Goldschmidt
-  * tcp.c: Fixed bug #30565 (tcp_connect() check bound list): that check did no
-    harm but never did anything
-  
-  2010-07-21: Simon Goldschmidt
-  * ip.c: Fixed invalid fix for bug #30402 (CHECKSUM_GEN_IP_INLINE does not
-    add IP options)
-
-  2010-07-16: Kieran Mansley
-  * msg_in.c: Fixed SNMP ASN constant defines to not use ! operator 
-
-  2010-07-10: Simon Goldschmidt
-  * ip.c: Fixed bug #30402: CHECKSUM_GEN_IP_INLINE does not add IP options
-
-  2010-06-30: Simon Goldschmidt
-  * api_msg.c: fixed bug #30300 (shutdown parameter was not initialized in
-    netconn_delete)
-
-  2010-06-28: Kieran Mansley
-  * timers.c remove unportable printing of C function pointers
-
-  2010-06-24: Simon Goldschmidt
-  * init.c, timers.c/.h, opt.h, memp_std.h: From patch #7221: added flag
-    NO_SYS_NO_TIMERS to drop timer support for NO_SYS==1 for easier upgrading
-
-  2010-06-24: Simon Goldschmidt
-  * api(_lib).c/.h, api_msg.c/.h, sockets.c/.h: Fixed bug #10088: Correctly
-    implemented shutdown at socket level.
-
-  2010-06-21: Simon Goldschmidt
-  * pbuf.c/.h, ip_frag.c/.h, opt.h, memp_std.h: Fixed bug #29361 (ip_frag has
-    problems with zero-copy DMA MACs) by adding custom pbufs and implementing
-    custom pbufs that reference other (original) pbufs. Additionally set
-    IP_FRAG_USES_STATIC_BUF=0 as default to be on the safe side.
-
-  2010-06-15: Simon Goldschmidt
-  * dhcp.c: Fixed bug #29970: DHCP endian issue parsing option responses
-
-  2010-06-14: Simon Goldschmidt
-  * autoip.c: Fixed bug #30039: AutoIP does not reuse previous addresses
-
-  2010-06-12: Simon Goldschmidt
-  * dhcp.c: Fixed bug #30038: dhcp_network_changed doesn't reset AUTOIP coop
-    state
-
-  2010-05-17: Simon Goldschmidt
-  * netdb.c: Correctly NULL-terminate h_addr_list
-
-  2010-05-16: Simon Goldschmidt
-  * def.h/.c: changed the semantics of LWIP_PREFIX_BYTEORDER_FUNCS to prevent
-    "symbol already defined" i.e. when linking to winsock
-
-  2010-05-05: Simon Goldschmidt
-  * def.h, timers.c: Fixed bug #29769 (sys_check_timeouts: sys_now() may
-    overflow)
-
-  2010-04-21: Simon Goldschmidt
-  * api_msg.c: Fixed bug #29617 (sometime cause stall on delete listening
-    connection)
-
-  2010-03-28: Luca Ceresoli
-  * ip_addr.c/.h: patch #7143: Add a few missing const qualifiers
-
-  2010-03-27: Luca Ceresoli
-  * mib2.c: patch #7130: remove meaningless const qualifiers
-
-  2010-03-26: Simon Goldschmidt
-  * tcp_out.c: Make LWIP_NETIF_TX_SINGLE_PBUF work for TCP, too
-
-  2010-03-26: Simon Goldschmidt
-  * various files: Fixed compiling with different options disabled (TCP/UDP),
-    triggered by bug #29345; don't allocate acceptmbox if LWIP_TCP is disabled
-
-  2010-03-25: Simon Goldschmidt
-  * sockets.c: Fixed bug #29332: lwip_select() processes readset incorrectly
-
-  2010-03-25: Simon Goldschmidt
-  * tcp_in.c, test_tcp_oos.c: Fixed bug #29080: Correctly handle remote side
-    overrunning our rcv_wnd in ooseq case.
-
-  2010-03-22: Simon Goldschmidt
-  * tcp.c: tcp_listen() did not copy the pcb's prio.
-
-  2010-03-19: Simon Goldschmidt
-  * snmp_msg.c: Fixed bug #29256: SNMP Trap address was not correctly set
-
-  2010-03-14: Simon Goldschmidt
-  * opt.h, etharp.h: Fixed bug #29148 (Incorrect PBUF_POOL_BUFSIZE for ports
-    where ETH_PAD_SIZE > 0) by moving definition of ETH_PAD_SIZE to opt.h
-    and basing PBUF_LINK_HLEN on it.
-
-  2010-03-08: Simon Goldschmidt
-  * netif.c, ipv4/ip.c: task #10241 (AutoIP: don't break existing connections
-    when assiging routable address): when checking incoming packets and
-    aborting existing connection on address change, filter out link-local
-    addresses.
-
-  2010-03-06: Simon Goldschmidt
-  * sockets.c: Fixed LWIP_NETIF_TX_SINGLE_PBUF for LWIP_TCPIP_CORE_LOCKING
-
-  2010-03-06: Simon Goldschmidt
-  * ipv4/ip.c: Don't try to forward link-local addresses
-
-  2010-03-06: Simon Goldschmidt
-  * etharp.c: Fixed bug #29087: etharp: don't send packets for LinkLocal-
-    addresses to gw
-
-  2010-03-05: Simon Goldschmidt
-  * dhcp.c: Fixed bug #29072: Correctly set ciaddr based on message-type
-    and state.
-
-  2010-03-05: Simon Goldschmidt
-  * api_msg.c: Correctly set TCP_WRITE_FLAG_MORE when netconn_write is split
-    into multiple calls to tcp_write.    
-
-  2010-02-21: Simon Goldschmidt
-  * opt.h, mem.h, dns.c: task #10140: Remove DNS_USES_STATIC_BUF (keep
-    the implementation of DNS_USES_STATIC_BUF==1)
-
-  2010-02-20: Simon Goldschmidt
-  * tcp.h, tcp.c, tcp_in.c, tcp_out.c: Task #10088: Correctly implement
-    close() vs. shutdown(). Now the application does not get any more
-    recv callbacks after calling tcp_close(). Added tcp_shutdown().
-
-  2010-02-19: Simon Goldschmidt
-  * mem.c/.h, pbuf.c: Renamed mem_realloc() to mem_trim() to prevent
-    confusion with realloc()
-
-  2010-02-15: Simon Goldschmidt/Stephane Lesage
-  * netif.c/.h: Link status does not depend on LWIP_NETIF_LINK_CALLBACK
-    (fixes bug #28899)
-
-  2010-02-14: Simon Goldschmidt
-  * netif.c: Fixed bug #28877 (Duplicate ARP gratuitous packet with
-    LWIP_NETIF_LINK_CALLBACK set on) by only sending if both link- and
-    admin-status of a netif are up
-
-  2010-02-14: Simon Goldschmidt
-  * opt.h: Disable ETHARP_TRUST_IP_MAC by default since it slows down packet
-    reception and is not really necessary
-
-  2010-02-14: Simon Goldschmidt
-  * etharp.c/.h: Fixed ARP input processing: only add a new entry if a
-    request was directed as us (RFC 826, Packet Reception), otherwise
-    only update existing entries; internalized some functions
-
-  2010-02-14: Simon Goldschmidt
-  * netif.h, etharp.c, tcpip.c: Fixed bug #28183 (ARP and TCP/IP cannot be
-    disabled on netif used for PPPoE) by adding a new netif flag
-    (NETIF_FLAG_ETHERNET) that tells the stack the device is an ethernet
-    device but prevents usage of ARP (so that ethernet_input can be used
-    for PPPoE).
-
-  2010-02-12: Simon Goldschmidt
-  * netif.c: netif_set_link_up/down: only do something if the link state
-    actually changes
-
-  2010-02-12: Simon Goldschmidt/Stephane Lesage
-  * api_msg.c: Fixed bug #28865 (Cannot close socket/netconn in non-blocking
-    connect)
-
-  2010-02-12: Simon Goldschmidt
-  * mem.h: Fixed bug #28866 (mem_realloc function defined in mem.h)
-
-  2010-02-09: Simon Goldschmidt
-  * api_lib.c, api_msg.c, sockets.c, api.h, api_msg.h: Fixed bug #22110
-   (recv() makes receive window update for data that wasn't received by
-    application)
-
-  2010-02-09: Simon Goldschmidt/Stephane Lesage
-  * sockets.c: Fixed bug #28853 (lwip_recvfrom() returns 0 on receive time-out
-    or any netconn_recv() error)
-
-  2010-02-09: Simon Goldschmidt
-  * ppp.c: task #10154 (PPP: Update snmp in/out counters for tx/rx packets)
-
-  2010-02-09: Simon Goldschmidt
-  * netif.c: For loopback packets, adjust the stats- and snmp-counters
-    for the loopback netif.
-
-  2010-02-08: Simon Goldschmidt
-  * igmp.c/.h, ip.h: Moved most defines from igmp.h to igmp.c for clarity
-    since they are not used anywhere else.
-
-  2010-02-08: Simon Goldschmidt (Stéphane Lesage)
-  * igmp.c, igmp.h, stats.c, stats.h: Improved IGMP stats
-    (patch from bug #28798)
-
-  2010-02-08: Simon Goldschmidt (Stéphane Lesage)
-  * igmp.c: Fixed bug #28798 (Error in "Max Response Time" processing) and
-    another bug when LWIP_RAND() returns zero.
-
-  2010-02-04: Simon Goldschmidt
-  * nearly every file: Use macros defined in ip_addr.h (some of them new)
-    to work with IP addresses (preparation for bug #27352 - Change ip_addr
-    from struct to typedef (u32_t) - and better code).
-
-  2010-01-31: Simon Goldschmidt
-  * netif.c: Don't call the link-callback from netif_set_up/down() since
-    this invalidly retriggers DHCP.
-
-  2010-01-29: Simon Goldschmidt
-  * ip_addr.h, inet.h, def.h, inet.c, def.c, more: Cleanly separate the
-    portability file inet.h and its contents from the stack: moved htonX-
-    functions to def.h (and the new def.c - they are not ipv4 dependent),
-    let inet.h depend on ip_addr.h and not the other way round.
-    This fixes bug #28732.
-
-  2010-01-28: Kieran Mansley
-  * tcp.c: Ensure ssthresh >= 2*MSS
-
-  2010-01-27: Simon Goldschmidt
-  * tcp.h, tcp.c, tcp_in.c: Fixed bug #27871: Calling tcp_abort() in recv
-    callback can lead to accessing unallocated memory. As a consequence,
-    ERR_ABRT means the application has called tcp_abort()!
-
-  2010-01-25: Simon Goldschmidt
-  * snmp_structs.h, msg_in.c: Partly fixed bug #22070 (MIB_OBJECT_WRITE_ONLY
-    not implemented in SNMP): write-only or not-accessible are still
-    returned by getnext (though not by get)
-
-  2010-01-24: Simon Goldschmidt
-  * snmp: Renamed the private mib node from 'private' to 'mib_private' to
-    not use reserved C/C++ keywords
-
-  2010-01-23: Simon Goldschmidt
-  * sockets.c: Fixed bug #28716: select() returns 0 after waiting for less
-    than 1 ms
-
-  2010-01-21: Simon Goldschmidt
-  * tcp.c, api_msg.c: Fixed bug #28651 (tcp_connect: no callbacks called
-    if tcp_enqueue fails) both in raw- and netconn-API
-
-  2010-01-19: Simon Goldschmidt
-  * api_msg.c: Fixed bug #27316: netconn: Possible deadlock in err_tcp
-
-  2010-01-18: Iordan Neshev/Simon Goldschmidt
-  * src/netif/ppp: reorganised PPP sourcecode to 2.3.11 including some
-    bugfix backports from 2.4.x.
-
-  2010-01-18: Simon Goldschmidt
-  * mem.c: Fixed bug #28679: mem_realloc calculates mem_stats wrong
-
-  2010-01-17: Simon Goldschmidt
-  * api_lib.c, api_msg.c, (api_msg.h, api.h, sockets.c, tcpip.c):
-    task #10102: "netconn: clean up conn->err threading issues" by adding
-    error return value to struct api_msg_msg
-
-  2010-01-17: Simon Goldschmidt
-  * api.h, api_lib.c, sockets.c: Changed netconn_recv() and netconn_accept()
-    to return err_t (bugs #27709 and #28087)
-
-  2010-01-14: Simon Goldschmidt
-  * ...: Use typedef for function prototypes throughout the stack.
-
-  2010-01-13: Simon Goldschmidt
-  * api_msg.h/.c, api_lib.c: Fixed bug #26672 (close connection when receive
-    window = 0) by correctly draining recvmbox/acceptmbox
-
-  2010-01-11: Simon Goldschmidt
-  * pap.c: Fixed bug #13315 (PPP PAP authentication can result in
-    erroneous callbacks) by copying the code from recent pppd
-
-  2010-01-10: Simon Goldschmidt
-  * raw.c: Fixed bug #28506 (raw_bind should filter received packets)
-
-  2010-01-10: Simon Goldschmidt
-  * tcp.h/.c: bug #28127 (remove call to tcp_output() from tcp_ack(_now)())
-
-  2010-01-08: Simon Goldschmidt
-  * sockets.c: Fixed bug #28519 (lwip_recvfrom bug with len > 65535)
-
-  2010-01-08: Simon Goldschmidt
-  * dns.c: Copy hostname for DNS_LOCAL_HOSTLIST_IS_DYNAMIC==1 since string
-    passed to dns_local_addhost() might be volatile
-
-  2010-01-07: Simon Goldschmidt
-  * timers.c, tcp.h: Call tcp_timer_needed() with NO_SYS==1, too
-
-  2010-01-06: Simon Goldschmidt
-  * netdb.h: Fixed bug #28496: missing include guards in netdb.h
-
-  2009-12-31: Simon Goldschmidt
-  * many ppp files: Reorganised PPP source code from ucip structure to pppd
-    structure to easily compare our code against the pppd code (around v2.3.1)
-
-  2009-12-27: Simon Goldschmidt
-  * tcp_in.c: Another fix for bug #28241 (ooseq processing) and adapted
-    unit test
-
-
-(STABLE-1.3.2)
-
-  ++ New features:
-
-  2009-10-27 Simon Goldschmidt/Stephan Lesage
-  * netifapi.c/.h: Added netifapi_netif_set_addr()
-
-  2009-10-07 Simon Goldschmidt/Fabian Koch
-  * api_msg.c, netbuf.c/.h, opt.h: patch #6888: Patch for UDP Netbufs to
-    support dest-addr and dest-port (optional: LWIP_NETBUF_RECVINFO)
-
-  2009-08-26 Simon Goldschmidt/Simon Kallweit
-  * slipif.c/.h: bug #26397: SLIP polling support
-
-  2009-08-25 Simon Goldschmidt
-  * opt.h, etharp.h/.c: task #9033: Support IEEE 802.1q tagged frame (VLAN),
-    New configuration options ETHARP_SUPPORT_VLAN and ETHARP_VLAN_CHECK.
-
-  2009-08-25 Simon Goldschmidt
-  * ip_addr.h, netdb.c: patch #6900: added define ip_ntoa(struct ip_addr*)
-
-  2009-08-24 Jakob Stoklund Olesen
-  * autoip.c, dhcp.c, netif.c: patch #6725: Teach AutoIP and DHCP to respond
-    to netif_set_link_up().
-
-  2009-08-23 Simon Goldschmidt
-  * tcp.h/.c: Added function tcp_debug_state_str() to convert a tcp state
-    to a human-readable string.
-
-  ++ Bugfixes:
-
-  2009-12-24: Kieran Mansley
-  * tcp_in.c Apply patches from Oleg Tyshev to improve OOS processing
-    (BUG#28241)
-
-  2009-12-06: Simon Goldschmidt
-  * ppp.h/.c: Fixed bug #27079 (Yet another leak in PPP): outpacket_buf can
-    be statically allocated (like in ucip)
-
-  2009-12-04: Simon Goldschmidt (patch by Ioardan Neshev)
-  * pap.c: patch #6969: PPP: missing PAP authentication UNTIMEOUT
-
-  2009-12-03: Simon Goldschmidt
-  * tcp.h, tcp_in.c, tcp_out.c: Fixed bug #28106: dup ack for fast retransmit
-    could have non-zero length
-
-  2009-12-02: Simon Goldschmidt
-  * tcp_in.c: Fixed bug #27904: TCP sends too many ACKs: delay resetting
-    tcp_input_pcb until after calling the pcb's callbacks
-
-  2009-11-29: Simon Goldschmidt
-  * tcp_in.c: Fixed bug #28054: Two segments with FIN flag on the out-of-
-    sequence queue, also fixed PBUF_POOL leak in the out-of-sequence code
-
-  2009-11-29: Simon Goldschmidt
-  * pbuf.c: Fixed bug #28064: pbuf_alloc(PBUF_POOL) is not thread-safe by
-    queueing a call into tcpip_thread to free ooseq-bufs if the pool is empty
-
-  2009-11-26: Simon Goldschmidt
-  * tcp.h: Fixed bug #28098: Nagle can prevent fast retransmit from sending
-    segment
-
-  2009-11-26: Simon Goldschmidt
-  * tcp.h, sockets.c: Fixed bug #28099: API required to disable Nagle
-    algorithm at PCB level
-
-  2009-11-22: Simon Goldschmidt
-  * tcp_out.c: Fixed bug #27905: FIN isn't combined with data on unsent
-
-  2009-11-22: Simon Goldschmidt (suggested by Bill Auerbach)
-  * tcp.c: tcp_alloc: prevent increasing stats.err for MEMP_TCP_PCB when
-    reusing time-wait pcb
-
-  2009-11-20: Simon Goldschmidt (patch by Albert Bartel)
-  * sockets.c: Fixed bug #28062: Data received directly after accepting
-    does not wake up select
-
-  2009-11-11: Simon Goldschmidt
-  * netdb.h: Fixed bug #27994: incorrect define for freeaddrinfo(addrinfo)
-
-  2009-10-30: Simon Goldschmidt
-  * opt.h: Increased default value for TCP_MSS to 536, updated default
-    value for TCP_WND to 4*TCP_MSS to keep delayed ACK working.
-
-  2009-10-28: Kieran Mansley
-  * tcp_in.c, tcp_out.c, tcp.h: re-work the fast retransmission code
-    to follow algorithm from TCP/IP Illustrated
-
-  2009-10-27: Kieran Mansley
-  * tcp_in.c: fix BUG#27445: grow cwnd with every duplicate ACK
-
-  2009-10-25: Simon Goldschmidt
-  * tcp.h: bug-fix in the TCP_EVENT_RECV macro (has to call tcp_recved if
-    pcb->recv is NULL to keep rcv_wnd correct)
-
-  2009-10-25: Simon Goldschmidt
-  * tcp_in.c: Fixed bug #26251: RST process in TIME_WAIT TCP state
-
-  2009-10-23: Simon Goldschmidt (David Empson)
-  * tcp.c: Fixed bug #27783: Silly window avoidance for small window sizes
-
-  2009-10-21: Simon Goldschmidt
-  * tcp_in.c: Fixed bug #27215: TCP sent() callback gives leading and
-    trailing 1 byte len (SYN/FIN)
-
-  2009-10-21: Simon Goldschmidt
-  * tcp_out.c: Fixed bug #27315: zero window probe and FIN
-
-  2009-10-19: Simon Goldschmidt
-  * dhcp.c/.h: Minor code simplification (don't store received pbuf, change
-    conditional code to assert where applicable), check pbuf length before
-    testing for valid reply
-
-  2009-10-19: Simon Goldschmidt
-  * dhcp.c: Removed most calls to udp_connect since they aren't necessary
-    when using udp_sendto_if() - always stay connected to IP_ADDR_ANY.
-
-  2009-10-16: Simon Goldschmidt
-  * ip.c: Fixed bug #27390: Source IP check in ip_input() causes it to drop
-    valid DHCP packets -> allow 0.0.0.0 as source address when LWIP_DHCP is
-    enabled
-
-  2009-10-15: Simon Goldschmidt (Oleg Tyshev)
-  * tcp_in.c: Fixed bug #27329: dupacks by unidirectional data transmit
-
-  2009-10-15: Simon Goldschmidt
-  * api_lib.c: Fixed bug #27709: conn->err race condition on netconn_recv()
-    timeout
-
-  2009-10-15: Simon Goldschmidt
-  * autoip.c: Fixed bug #27704: autoip starts with wrong address
-    LWIP_AUTOIP_CREATE_SEED_ADDR() returned address in host byte order instead
-    of network byte order
-
-  2009-10-11 Simon Goldschmidt (Jörg Kesten)
-  * tcp_out.c: Fixed bug #27504: tcp_enqueue wrongly concatenates segments
-    which are not consecutive when retransmitting unacked segments
-
-  2009-10-09 Simon Goldschmidt
-  * opt.h: Fixed default values of some stats to only be enabled if used
-    Fixes bug #27338: sys_stats is defined when NO_SYS = 1
-
-  2009-08-30 Simon Goldschmidt
-  * ip.c: Fixed bug bug #27345: "ip_frag() does not use the LWIP_NETIF_LOOPBACK
-    function" by checking for loopback before calling ip_frag
-
-  2009-08-25 Simon Goldschmidt
-  * dhcp.c: fixed invalid dependency to etharp_query if DHCP_DOES_ARP_CHECK==0
-
-  2009-08-23 Simon Goldschmidt
-  * ppp.c: bug #27078: Possible memory leak in pppInit()
-
-  2009-08-23 Simon Goldschmidt
-  * netdb.c, dns.c: bug #26657: DNS, if host name is "localhost", result
-    is error.
-
-  2009-08-23 Simon Goldschmidt
-  * opt.h, init.c: bug #26649: TCP fails when TCP_MSS > TCP_SND_BUF
-    Fixed wrong parenthesis, added check in init.c
-
-  2009-08-23 Simon Goldschmidt
-  * ppp.c: bug #27266: wait-state debug message in pppMain occurs every ms
-
-  2009-08-23 Simon Goldschmidt
-  * many ppp files: bug #27267: Added include to string.h where needed
-
-  2009-08-23 Simon Goldschmidt
-  * tcp.h: patch #6843: tcp.h macro optimization patch (for little endian)
-
-
-(STABLE-1.3.1)
-
-  ++ New features:
-
-  2009-05-10 Simon Goldschmidt
-  * opt.h, sockets.c, pbuf.c, netbuf.h, pbuf.h: task #7013: Added option
-    LWIP_NETIF_TX_SINGLE_PBUF to try to create transmit packets from only
-    one pbuf to help MACs that don't support scatter-gather DMA.
-
-  2009-05-09 Simon Goldschmidt
-  * icmp.h, icmp.c: Shrinked ICMP code, added option to NOT check icoming
-    ECHO pbuf for size (just use it): LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN
-
-  2009-05-05 Simon Goldschmidt, Jakob Stoklund Olesen
-  * ip.h, ip.c: Added ip_current_netif() & ip_current_header() to receive
-    extended info about the currently received packet.
-
-  2009-04-27 Simon Goldschmidt
-  * sys.h: Made SYS_LIGHTWEIGHT_PROT and sys_now() work with NO_SYS=1
-
-  2009-04-25 Simon Goldschmidt
-  * mem.c, opt.h: Added option MEM_USE_POOLS_TRY_BIGGER_POOL to try the next
-    bigger malloc pool if one is empty (only usable with MEM_USE_POOLS).
-
-  2009-04-21 Simon Goldschmidt
-  * dns.c, init.c, dns.h, opt.h: task #7507, patch #6786: DNS supports static
-    hosts table. New configuration options DNS_LOCAL_HOSTLIST and
-    DNS_LOCAL_HOSTLIST_IS_DYNAMIC. Also, DNS_LOOKUP_LOCAL_EXTERN() can be defined
-    as an external function for lookup.
-
-  2009-04-15 Simon Goldschmidt
-  * dhcp.c: patch #6763: Global DHCP XID can be redefined to something more unique
-
-  2009-03-31 Kieran Mansley
-  * tcp.c, tcp_out.c, tcp_in.c, sys.h, tcp.h, opts.h: add support for
-    TCP timestamp options, off by default.  Rework tcp_enqueue() to
-    take option flags rather than specified option data
-
-  2009-02-18 Simon Goldschmidt
-  * cc.h: Added printf formatter for size_t: SZT_F
-
-  2009-02-16 Simon Goldschmidt (patch by Rishi Khan)
-  * icmp.c, opt.h: patch #6539: (configurable) response to broadcast- and multicast
-    pings
-
-  2009-02-12 Simon Goldschmidt
-  * init.h: Added LWIP_VERSION to get the current version of the stack
-
-  2009-02-11 Simon Goldschmidt (suggested by Gottfried Spitaler)
-  * opt.h, memp.h/.c: added MEMP_MEM_MALLOC to use mem_malloc/mem_free instead
-    of the pool allocator (can save code size with MEM_LIBC_MALLOC if libc-malloc
-    is otherwise used)
-
-  2009-01-28 Jonathan Larmour (suggested by Bill Bauerbach)
-  * ipv4/inet_chksum.c, ipv4/lwip/inet_chksum.h: inet_chksum_pseudo_partial()
-  is only used by UDPLITE at present, so conditionalise it.
-
-  2008-12-03 Simon Goldschmidt (base on patch from Luca Ceresoli)
-  * autoip.c: checked in (slightly modified) patch #6683: Customizable AUTOIP
-    "seed" address. This should reduce AUTOIP conflicts if
-    LWIP_AUTOIP_CREATE_SEED_ADDR is overridden.
-
-  2008-10-02 Jonathan Larmour and Rishi Khan
-  * sockets.c (lwip_accept): Return EWOULDBLOCK if would block on non-blocking
-    socket.
-
-  2008-06-30 Simon Goldschmidt
-  * mem.c, opt.h, stats.h: fixed bug #21433: Calling mem_free/pbuf_free from
-    interrupt context isn't safe: LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT allows
-    mem_free to run between mem_malloc iterations. Added illegal counter for
-    mem stats.
-
-  2008-06-27 Simon Goldschmidt
-  * stats.h/.c, some other files: patch #6483: stats module improvement:
-    Added defines to display each module's statistic individually, added stats
-    defines for MEM, MEMP and SYS modules, removed (unused) rexmit counter.
-
-  2008-06-17 Simon Goldschmidt
-  * err.h: patch #6459: Made err_t overridable to use a more efficient type
-    (define LWIP_ERR_T in cc.h)
-
-  2008-06-17 Simon Goldschmidt
-  * slipif.c: patch #6480: Added a configuration option for slipif for symmetry
-    to loopif
-
-  2008-06-17 Simon Goldschmidt (patch by Luca Ceresoli)
-  * netif.c, loopif.c, ip.c, netif.h, loopif.h, opt.h: Checked in slightly
-    modified version of patch # 6370: Moved loopif code to netif.c so that
-    loopback traffic is supported on all netifs (all local IPs).
-    Added option to limit loopback packets for each netifs.
-
-
-  ++ Bugfixes:
-  2009-08-12 Kieran Mansley
-  * tcp_in.c, tcp.c: Fix bug #27209: handle trimming of segments when
-    out of window or out of order properly
-
-  2009-08-12 Kieran Mansley
-  * tcp_in.c: Fix bug #27199: use snd_wl2 instead of snd_wl1
-
-  2009-07-28 Simon Goldschmidt
-  * mem.h: Fixed bug #27105: "realloc() cannot replace mem_realloc()"s
-
-  2009-07-27 Kieran Mansley
-  * api.h api_msg.h netdb.h sockets.h: add missing #include directives
-
-  2009-07-09 Kieran Mansley
-  * api_msg.c, sockets.c, api.h: BUG23240 use signed counters for
-    recv_avail and don't increment counters until message successfully
-    sent to mbox
-
-  2009-06-25 Kieran Mansley
-  * api_msg.c api.h: BUG26722: initialise netconn write variables 
-    in netconn_alloc
-
-  2009-06-25 Kieran Mansley
-  * tcp.h: BUG26879: set ret value in TCP_EVENT macros when function is not set
-
-  2009-06-25 Kieran Mansley
-  * tcp.c, tcp_in.c, tcp_out.c, tcp.h: BUG26301 and BUG26267: correct
-    simultaneous close behaviour, and make snd_nxt have the same meaning 
-    as in the RFCs.
-
-  2009-05-12 Simon Goldschmidt
-  * etharp.h, etharp.c, netif.c: fixed bug #26507: "Gratuitous ARP depends on
-    arp_table / uses etharp_query" by adding etharp_gratuitous()
-
-  2009-05-12 Simon Goldschmidt
-  * ip.h, ip.c, igmp.c: bug #26487: Added ip_output_if_opt that can add IP options
-    to the IP header (used by igmp_ip_output_if)
-
-  2009-05-06 Simon Goldschmidt
-  * inet_chksum.c: On little endian architectures, use LWIP_PLATFORM_HTONS (if
-    defined) for SWAP_BYTES_IN_WORD to speed up checksumming.
-
-  2009-05-05 Simon Goldschmidt
-  * sockets.c: bug #26405: Prematurely released semaphore causes lwip_select()
-    to crash
-
-  2009-05-04 Simon Goldschmidt
-  * init.c: snmp was not initialized in lwip_init()
-
-  2009-05-04 Frédéric Bernon
-  * dhcp.c, netbios.c: Changes if IP_SOF_BROADCAST is enabled.
-
-  2009-05-03 Simon Goldschmidt
-  * tcp.h: bug #26349: Nagle algorithm doesn't send although segment is full
-    (and unsent->next == NULL)
-
-  2009-05-02 Simon Goldschmidt
-  * tcpip.h, tcpip.c: fixed tcpip_untimeout (does not need the time, broken after
-    1.3.0 in CVS only) - fixes compilation of ppp_oe.c
-
-  2009-05-02 Simon Goldschmidt
-  * msg_in.c: fixed bug #25636: SNMPSET value is ignored for integer fields
-
-  2009-05-01 Simon Goldschmidt
-  * pap.c: bug #21680: PPP upap_rauthnak() drops legal NAK packets
-
-  2009-05-01 Simon Goldschmidt
-  * ppp.c: bug #24228: Memory corruption with PPP and DHCP
-
-  2009-04-29 Frédéric Bernon
-  * raw.c, udp.c, init.c, opt.h, ip.h, sockets.h: bug #26309: Implement the
-    SO(F)_BROADCAST filter for all API layers. Avoid the unindented reception
-    of broadcast packets even when this option wasn't set. Port maintainers
-    which want to enable this filter have to set IP_SOF_BROADCAST=1 in opt.h.
-    If you want this option also filter broadcast on recv operations, you also
-    have to set IP_SOF_BROADCAST_RECV=1 in opt.h.
-
-  2009-04-28 Simon Goldschmidt, Jakob Stoklund Olesen
-  * dhcp.c: patch #6721, bugs #25575, #25576: Some small fixes to DHCP and
-    DHCP/AUTOIP cooperation
-
-  2009-04-25 Simon Goldschmidt, Oleg Tyshev
-  * tcp_out.c: bug #24212: Deadlocked tcp_retransmit due to exceeded pcb->cwnd
-    Fixed by sorting the unsent and unacked queues (segments are inserted at the
-    right place in tcp_output and tcp_rexmit).
-
-  2009-04-25 Simon Goldschmidt
-  * memp.c, mem.c, memp.h, mem_std.h: bug #26213 "Problem with memory allocation
-    when debugging": memp_sizes contained the wrong sizes (including sanity
-    regions); memp pools for MEM_USE_POOLS were too small
-
-  2009-04-24 Simon Goldschmidt, Frédéric Bernon
-  * inet.c: patch #6765: Fix a small problem with the last changes (incorrect
-    behavior, with with ip address string not ended by a '\0', a space or a
-    end of line)
-
-  2009-04-19 Simon Goldschmidt
-  * rawapi.txt: Fixed bug #26069: Corrected documentation: if tcp_connect fails,
-    pcb->err is called, not pcb->connected (with an error code).
-
-  2009-04-19 Simon Goldschmidt
-  * tcp_out.c: Fixed bug #26236: "TCP options (timestamp) don't work with
-    no-copy-tcpwrite": deallocate option data, only concat segments with same flags
-
-  2009-04-19 Simon Goldschmidt
-  * tcp_out.c: Fixed bug #25094: "Zero-length pbuf" (options are now allocated
-    in the header pbuf, not the data pbuf)
-
-  2009-04-18 Simon Goldschmidt
-  * api_msg.c: fixed bug #25695: Segmentation fault in do_writemore()
-
-  2009-04-15 Simon Goldschmidt
-  * sockets.c: tried to fix bug #23559: lwip_recvfrom problem with tcp
-
-  2009-04-15 Simon Goldschmidt
-  * dhcp.c: task #9192: mem_free of dhcp->options_in and dhcp->msg_in
-
-  2009-04-15 Simon Goldschmidt
-  * ip.c, ip6.c, tcp_out.c, ip.h: patch #6808: Add a utility function
-    ip_hinted_output() (for smaller code mainly)
-
-  2009-04-15 Simon Goldschmidt
-  * inet.c: patch #6765: Supporting new line characters in inet_aton()
-
-  2009-04-15 Simon Goldschmidt
-  * dhcp.c: patch #6764: DHCP rebind and renew did not send hostnam option;
-    Converted constant OPTION_MAX_MSG_SIZE to netif->mtu, check if netif->mtu
-    is big enough in dhcp_start
-
-  2009-04-15 Simon Goldschmidt
-  * netbuf.c: bug #26027: netbuf_chain resulted in pbuf memory leak
-
-  2009-04-15 Simon Goldschmidt
-  * sockets.c, ppp.c: bug #25763: corrected 4 occurrences of SMEMCPY to MEMCPY
-
-  2009-04-15 Simon Goldschmidt
-  * sockets.c: bug #26121: set_errno can be overridden
-
-  2009-04-09 Kieran Mansley (patch from Luca Ceresoli <lucaceresoli>)
-  * init.c, opt.h: Patch#6774 TCP_QUEUE_OOSEQ breaks compilation when
-    LWIP_TCP==0
-
-  2009-04-09 Kieran Mansley (patch from Roy Lee <roylee17>)
-  * tcp.h: Patch#6802 Add do-while-clauses to those function like
-    macros in tcp.h
-
-  2009-03-31 Kieran Mansley
-  * tcp.c, tcp_in.c, tcp_out.c, tcp.h, opt.h: Rework the way window
-    updates are calculated and sent (BUG20515)
-
-  * tcp_in.c: cope with SYN packets received during established states,
-    and retransmission of initial SYN.
-
-  * tcp_out.c: set push bit correctly when tcp segments are merged
-
-  2009-03-27 Kieran Mansley
-  * tcp_out.c set window correctly on probes (correcting change made
-    yesterday)
-
-  2009-03-26 Kieran Mansley
-  * tcp.c, tcp_in.c, tcp.h: add tcp_abandon() to cope with dropping
-    connections where no reset required (bug #25622)
-
-  * tcp_out.c: set TCP_ACK flag on keepalive and zero window probes 
-    (bug #20779)
-
-  2009-02-18 Simon Goldschmidt (Jonathan Larmour and Bill Auerbach)
-  * ip_frag.c: patch #6528: the buffer used for IP_FRAG_USES_STATIC_BUF could be
-    too small depending on MEM_ALIGNMENT
-
-  2009-02-16 Simon Goldschmidt
-  * sockets.h/.c, api_*.h/.c: fixed arguments of socket functions to match the standard;
-    converted size argument of netconn_write to 'size_t'
-
-  2009-02-16 Simon Goldschmidt
-  * tcp.h, tcp.c: fixed bug #24440: TCP connection close problem on 64-bit host
-    by moving accept callback function pointer to TCP_PCB_COMMON
-
-  2009-02-12 Simon Goldschmidt
-  * dhcp.c: fixed bug #25345 (DHCPDECLINE is sent with "Maximum message size"
-    option)
-
-  2009-02-11 Simon Goldschmidt
-  * dhcp.c: fixed bug #24480 (releasing old udp_pdb and pbuf in dhcp_start)
-
-  2009-02-11 Simon Goldschmidt
-  * opt.h, api_msg.c: added configurable default valud for netconn->recv_bufsize:
-    RECV_BUFSIZE_DEFAULT (fixes bug #23726: pbuf pool exhaustion on slow recv())
-
-  2009-02-10 Simon Goldschmidt
-  * tcp.c: fixed bug #25467: Listen backlog is not reset on timeout in SYN_RCVD:
-    Accepts_pending is decrease on a corresponding listen pcb when a connection
-    in state SYN_RCVD is close.
-
-  2009-01-28 Jonathan Larmour
-  * pbuf.c: reclaim pbufs from TCP out-of-sequence segments if we run
-    out of pool pbufs.
-
-  2008-12-19 Simon Goldschmidt
-  * many files: patch #6699: fixed some warnings on platform where sizeof(int) == 2 
-
-  2008-12-10 Tamas Somogyi, Frédéric Bernon
-  * sockets.c: fixed bug #25051: lwip_recvfrom problem with udp: fromaddr and
-    port uses deleted netbuf.
-
-  2008-10-18 Simon Goldschmidt
-  * tcp_in.c: fixed bug ##24596: Vulnerability on faulty TCP options length
-    in tcp_parseopt
-
-  2008-10-15 Simon Goldschmidt
-  * ip_frag.c: fixed bug #24517: IP reassembly crashes on unaligned IP headers
-    by packing the struct ip_reass_helper.
-
-  2008-10-03 David Woodhouse, Jonathan Larmour
-  * etharp.c (etharp_arp_input): Fix type aliasing problem copying ip address.
-
-  2008-10-02 Jonathan Larmour
-  * dns.c: Hard-code structure sizes, to avoid issues on some compilers where
-    padding is included.
-
-  2008-09-30 Jonathan Larmour
-  * sockets.c (lwip_accept): check addr isn't NULL. If it's valid, do an
-    assertion check that addrlen isn't NULL.
-
-  2008-09-30 Jonathan Larmour
-  * tcp.c: Fix bug #24227, wrong error message in tcp_bind.
-
-  2008-08-26 Simon Goldschmidt
-  * inet.h, ip_addr.h: fixed bug #24132: Cross-dependency between ip_addr.h and
-    inet.h -> moved declaration of struct in_addr from ip_addr.h to inet.h
-
-  2008-08-14 Simon Goldschmidt
-  * api_msg.c: fixed bug #23847: do_close_internal references freed memory (when
-    tcp_close returns != ERR_OK)
-
-  2008-07-08 Frédéric Bernon
-  * stats.h: Fix some build bugs introduced with patch #6483 (missing some parameters
-    in macros, mainly if MEM_STATS=0 and MEMP_STATS=0).
-
-  2008-06-24 Jonathan Larmour
-  * tcp_in.c: Fix for bug #23693 as suggested by Art R. Ensure cseg is unused
-    if tcp_seg_copy fails.
-
-  2008-06-17 Simon Goldschmidt
-  * inet_chksum.c: Checked in some ideas of patch #6460 (loop optimizations)
-    and created defines for swapping bytes and folding u32 to u16.
-
-  2008-05-30 Kieran Mansley
-  * tcp_in.c Remove redundant "if" statement, and use real rcv_wnd
-    rather than rcv_ann_wnd when deciding if packets are in-window.
-    Contributed by <arasmussen@consultant.datasys.swri.edu>
-
-  2008-05-30 Kieran Mansley
-  * mem.h: Fix BUG#23254.  Change macro definition of mem_* to allow
-    passing as function pointers when MEM_LIBC_MALLOC is defined.
-
-  2008-05-09 Jonathan Larmour
-  * err.h, err.c, sockets.c: Fix bug #23119: Reorder timeout error code to
-    stop it being treated as a fatal error.
-
-  2008-04-15 Simon Goldschmidt
-  * dhcp.c: fixed bug #22804: dhcp_stop doesn't clear NETIF_FLAG_DHCP
-    (flag now cleared)
-
-  2008-03-27 Simon Goldschmidt
-  * mem.c, tcpip.c, tcpip.h, opt.h: fixed bug #21433 (Calling mem_free/pbuf_free
-    from interrupt context isn't safe): set LWIP_USE_HEAP_FROM_INTERRUPT to 1
-    in lwipopts.h or use pbuf_free_callback(p)/mem_free_callback(m) to free pbufs
-    or heap memory from interrupt context
-
-  2008-03-26 Simon Goldschmidt
-  * tcp_in.c, tcp.c: fixed bug #22249: division by zero could occur if a remote
-    host sent a zero mss as TCP option.
-
-
-(STABLE-1.3.0)
-
-  ++ New features:
-
-  2008-03-10 Jonathan Larmour
-  * inet_chksum.c: Allow choice of one of the sample algorithms to be
-    made from lwipopts.h. Fix comment on how to override LWIP_CHKSUM.
-
-  2008-01-22 Frédéric Bernon
-  * tcp.c, tcp_in.c, tcp.h, opt.h: Rename LWIP_CALCULATE_EFF_SEND_MSS in 
-    TCP_CALCULATE_EFF_SEND_MSS to have coherent TCP options names.
-
-  2008-01-14 Frédéric Bernon
-  * rawapi.txt, api_msg.c, tcp.c, tcp_in.c, tcp.h: changes for task #7675 "Enable
-    to refuse data on a TCP_EVENT_RECV call". Important, behavior changes for the
-    tcp_recv callback (see rawapi.txt).
-
-  2008-01-14 Frédéric Bernon, Marc Chaland
-  * ip.c: Integrate patch #6369" ip_input : checking before realloc".
-  
-  2008-01-12 Frédéric Bernon
-  * tcpip.h, tcpip.c, api.h, api_lib.c, api_msg.c, sockets.c: replace the field
-    netconn::sem per netconn::op_completed like suggested for the task #7490
-    "Add return value to sys_mbox_post".
-
-  2008-01-12 Frédéric Bernon
-  * api_msg.c, opt.h: replace DEFAULT_RECVMBOX_SIZE per DEFAULT_TCP_RECVMBOX_SIZE,
-    DEFAULT_UDP_RECVMBOX_SIZE and DEFAULT_RAW_RECVMBOX_SIZE (to optimize queues
-    sizes), like suggested for the task #7490 "Add return value to sys_mbox_post".
-
-  2008-01-10 Frédéric Bernon
-  * tcpip.h, tcpip.c: add tcpip_callback_with_block function for the task #7490
-    "Add return value to sys_mbox_post". tcpip_callback is always defined as
-    "blocking" ("block" parameter = 1).
-
-  2008-01-10 Frédéric Bernon
-  * tcpip.h, tcpip.c, api.h, api_lib.c, api_msg.c, sockets.c: replace the field
-    netconn::mbox (sys_mbox_t) per netconn::sem (sys_sem_t) for the task #7490
-    "Add return value to sys_mbox_post".
-
-  2008-01-05 Frédéric Bernon
-  * sys_arch.txt, api.h, api_lib.c, api_msg.h, api_msg.c, tcpip.c, sys.h, opt.h:
-    Introduce changes for task #7490 "Add return value to sys_mbox_post" with some
-    modifications in the sys_mbox api: sys_mbox_new take a "size" parameters which
-    indicate the number of pointers query by the mailbox. There is three defines
-    in opt.h to indicate sizes for tcpip::mbox, netconn::recvmbox, and for the 
-    netconn::acceptmbox. Port maintainers, you can decide to just add this new 
-    parameter in your implementation, but to ignore it to keep the previous behavior.
-    The new sys_mbox_trypost function return a value to know if the mailbox is
-    full or if the message is posted. Take a look to sys_arch.txt for more details.
-    This new function is used in tcpip_input (so, can be called in an interrupt
-    context since the function is not blocking), and in recv_udp and recv_raw.
-
-  2008-01-04 Frédéric Bernon, Simon Goldschmidt, Jonathan Larmour
-  * rawapi.txt, api.h, api_lib.c, api_msg.h, api_msg.c, sockets.c, tcp.h, tcp.c,
-    tcp_in.c, init.c, opt.h: rename backlog options with TCP_ prefix, limit the
-    "backlog" parameter in an u8_t, 0 is interpreted as "smallest queue", add
-    documentation in the rawapi.txt file.
-
-  2007-12-31 Kieran Mansley (based on patch from Per-Henrik Lundbolm)
-  * tcp.c, tcp_in.c, tcp_out.c, tcp.h: Add TCP persist timer
-
-  2007-12-31 Frédéric Bernon, Luca Ceresoli
-  * autoip.c, etharp.c: ip_addr.h: Integrate patch #6348: "Broadcast ARP packets
-    in autoip". The change in etharp_raw could be removed, since all calls to
-    etharp_raw use ethbroadcast for the "ethdst_addr" parameter. But it could be
-    wrong in the future.
-
-  2007-12-30 Frédéric Bernon, Tom Evans
-  * ip.c: Fix bug #21846 "LwIP doesn't appear to perform any IP Source Address
-    Filtering" reported by Tom Evans.
-
-  2007-12-21 Frédéric Bernon, Simon Goldschmidt, Jonathan Larmour
-  * tcp.h, opt.h, api.h, api_msg.h, tcp.c, tcp_in.c, api_lib.c, api_msg.c,
-    sockets.c, init.c: task #7252: Implement TCP listen backlog: Warning: raw API
-    applications have to call 'tcp_accepted(pcb)' in their accept callback to
-    keep accepting new connections.
-
-  2007-12-13 Frédéric Bernon
-  * api_msg.c, err.h, err.c, sockets.c, dns.c, dns.h: replace "enum dns_result"
-    by err_t type. Add a new err_t code "ERR_INPROGRESS".
-
-  2007-12-12 Frédéric Bernon
-  * dns.h, dns.c, opt.h: move DNS options to the "right" place. Most visibles
-    are the one which have ram usage.
-
-  2007-12-05 Frédéric Bernon
-  * netdb.c: add a LWIP_DNS_API_HOSTENT_STORAGE option to decide to use a static
-    set of variables (=0) or a local one (=1). In this last case, your port should
-    provide a function "struct hostent* sys_thread_hostent( struct hostent* h)"
-    which have to do a copy of "h" and return a pointer ont the "per-thread" copy.
-
-  2007-12-03 Simon Goldschmidt
-  * ip.c: ip_input: check if a packet is for inp first before checking all other
-    netifs on netif_list (speeds up packet receiving in most cases)
-
-  2007-11-30 Simon Goldschmidt
-  * udp.c, raw.c: task #7497: Sort lists (pcb, netif, ...) for faster access
-    UDP: move a (connected) pcb selected for input to the front of the list of
-    pcbs so that it is found faster next time. Same for RAW pcbs that have eaten
-    a packet.
-
-  2007-11-28 Simon Goldschmidt
-  * etharp.c, stats.c, stats.h, opt.h: Introduced ETHARP_STATS
-
-  2007-11-25 Simon Goldschmidt
-  * dhcp.c: dhcp_unfold_reply() uses pbuf_copy_partial instead of its own copy
-    algorithm.
-
-  2007-11-24 Simon Goldschmidt
-  * netdb.h, netdb.c, sockets.h/.c: Moved lwip_gethostbyname from sockets.c
-    to the new file netdb.c; included lwip_getaddrinfo.
-
-  2007-11-21 Simon Goldschmidt
-  * tcp.h, opt.h, tcp.c, tcp_in.c: implemented calculating the effective send-mss
-    based on the MTU of the netif used to send. Enabled by default. Disable by
-    setting LWIP_CALCULATE_EFF_SEND_MSS to 0. This fixes bug #21492.
-
-  2007-11-19 Frédéric Bernon
-  * api_msg.c, dns.h, dns.c: Implement DNS_DOES_NAME_CHECK option (check if name
-    received match the name query), implement DNS_USES_STATIC_BUF (the place where
-    copy dns payload to parse the response), return an error if there is no place
-    for a new query, and fix some minor problems.
-
-  2007-11-16 Simon Goldschmidt
-  * new files: ipv4/inet.c, ipv4/inet_chksum.c, ipv6/inet6.c
-    removed files: core/inet.c, core/inet6.c
-    Moved inet files into ipv4/ipv6 directory; splitted inet.c/inet.h into
-    inet and chksum part; changed includes in all lwIP files as appropriate
-
-  2007-11-16 Simon Goldschmidt
-  * api.h, api_msg.h, api_lib.c, api_msg.c, socket.h, socket.c: Added sequential
-    dns resolver function for netconn api (netconn_gethostbyname) and socket api
-    (gethostbyname/gethostbyname_r).
-
-  2007-11-15 Jim Pettinato, Frédéric Bernon
-  * opt.h, init.c, tcpip.c, dhcp.c, dns.h, dns.c: add DNS client for simple name
-    requests with RAW api interface. Initialization is done in lwip_init() with
-    build time options. DNS timer is added in tcpip_thread context. DHCP can set
-    DNS server ip addresses when options are received. You need to set LWIP_DNS=1
-    in your lwipopts.h file (LWIP_DNS=0 in opt.h). DNS_DEBUG can be set to get
-    some traces with LWIP_DEBUGF. Sanity check have been added. There is a "todo"
-    list with points to improve.
-
-  2007-11-06 Simon Goldschmidt
-  * opt.h, mib2.c: Patch #6215: added ifAdminStatus write support (if explicitly
-    enabled by defining SNMP_SAFE_REQUESTS to 0); added code to check link status
-    for ifOperStatus if LWIP_NETIF_LINK_CALLBACK is defined.
-
-  2007-11-06 Simon Goldschmidt
-  * api.h, api_msg.h and dependent files: Task #7410: Removed the need to include
-    core header files in api.h (ip/tcp/udp/raw.h) to hide the internal
-    implementation from netconn api applications.
-
-  2007-11-03 Frédéric Bernon
-  * api.h, api_lib.c, api_msg.c, sockets.c, opt.h: add SO_RCVBUF option for UDP &
-    RAW netconn. You need to set LWIP_SO_RCVBUF=1 in your lwipopts.h (it's disabled
-    by default). Netconn API users can use the netconn_recv_bufsize macro to access
-    it. This is a first release which have to be improve for TCP. Note it used the
-    netconn::recv_avail which need to be more "thread-safe" (note there is already
-    the problem for FIONREAD with lwip_ioctl/ioctlsocket).
-
-  2007-11-01 Frédéric Bernon, Marc Chaland
-  * sockets.h, sockets.c, api.h, api_lib.c, api_msg.h, api_msg.c, tcp.h, tcp_out.c:
-    Integrate "patch #6250 : MSG_MORE flag for send". MSG_MORE is used at socket api
-    layer, NETCONN_MORE at netconn api layer, and TCP_WRITE_FLAG_MORE at raw api
-    layer. This option enable to delayed TCP PUSH flag on multiple "write" calls.
-    Note that previous "copy" parameter for "write" APIs is now called "apiflags".
-
-  2007-10-24 Frédéric Bernon
-  * api.h, api_lib.c, api_msg.c: Add macro API_EVENT in the same spirit than 
-    TCP_EVENT_xxx macros to get a code more readable. It could also help to remove
-    some code (like we have talk in "patch #5919 : Create compile switch to remove
-    select code"), but it could be done later.
-
-  2007-10-08 Simon Goldschmidt
-  * many files: Changed initialization: many init functions are not needed any
-    more since we now rely on the compiler initializing global and static
-    variables to zero!
-
-  2007-10-06 Simon Goldschmidt
-  * ip_frag.c, memp.c, mib2.c, ip_frag.h, memp_std.h, opt.h: Changed IP_REASSEMBLY
-    to enqueue the received pbufs so that multiple packets can be reassembled
-    simultaneously and no static reassembly buffer is needed.
-
-  2007-10-05 Simon Goldschmidt
-  * tcpip.c, etharp.h, etharp.c: moved ethernet_input from tcpip.c to etharp.c so
-    all netifs (or ports) can use it.
-
-  2007-10-05 Frédéric Bernon
-  * netifapi.h, netifapi.c: add function netifapi_netif_set_default. Change the 
-    common function to reduce a little bit the footprint (for all functions using
-    only the "netif" parameter).
-
-  2007-10-03 Frédéric Bernon
-  * netifapi.h, netifapi.c: add functions netifapi_netif_set_up, netifapi_netif_set_down,
-    netifapi_autoip_start and netifapi_autoip_stop. Use a common function to reduce
-    a little bit the footprint (for all functions using only the "netif" parameter).
-
-  2007-09-15 Frédéric Bernon
-  * udp.h, udp.c, sockets.c: Changes for "#20503 IGMP Improvement". Add IP_MULTICAST_IF
-    option in socket API, and a new field "multicast_ip" in "struct udp_pcb" (for
-    netconn and raw API users), only if LWIP_IGMP=1. Add getsockopt processing for
-    IP_MULTICAST_TTL and IP_MULTICAST_IF.
-
-  2007-09-10 Frédéric Bernon
-  * snmp.h, mib2.c: enable to remove SNMP timer (which consumne several cycles
-    even when it's not necessary). snmp_agent.txt tell to call snmp_inc_sysuptime()
-    each 10ms (but, it's intrusive if you use sys_timeout feature). Now, you can
-    decide to call snmp_add_sysuptime(100) each 1000ms (which is bigger "step", but
-    call to a lower frequency). Or, you can decide to not call snmp_inc_sysuptime()
-    or snmp_add_sysuptime(), and to define the SNMP_GET_SYSUPTIME(sysuptime) macro.
-    This one is undefined by default in mib2.c. SNMP_GET_SYSUPTIME is called inside
-    snmp_get_sysuptime(u32_t *value), and enable to change "sysuptime" value only
-    when it's queried (any direct call to "sysuptime" is changed by a call to 
-    snmp_get_sysuptime).
-
-  2007-09-09 Frédéric Bernon, Bill Florac
-  * igmp.h, igmp.c, netif.h, netif.c, ip.c: To enable to have interfaces with IGMP,
-    and others without it, there is a new NETIF_FLAG_IGMP flag to set in netif->flags
-    if you want IGMP on an interface. igmp_stop() is now called inside netif_remove().
-    igmp_report_groups() is now called inside netif_set_link_up() (need to have
-    LWIP_NETIF_LINK_CALLBACK=1) to resend reports once the link is up (avoid to wait
-    the next query message to receive the matching multicast streams).
-
-  2007-09-08 Frédéric Bernon
-  * sockets.c, ip.h, api.h, tcp.h: declare a "struct ip_pcb" which only contains
-    IP_PCB. Add in the netconn's "pcb" union a "struct ip_pcb *ip;" (no size change).
-    Use this new field to access to common pcb fields (ttl, tos, so_options, etc...).
-    Enable to access to these fields with LWIP_TCP=0.
-
-  2007-09-05 Frédéric Bernon
-  * udp.c, ipv4/icmp.c, ipv4/ip.c, ipv6/icmp.c, ipv6/ip6.c, ipv4/icmp.h,
-    ipv6/icmp.h, opt.h: Integrate "task #7272 : LWIP_ICMP option". The new option
-    LWIP_ICMP enable/disable ICMP module inside the IP stack (enable per default).
-    Be careful, disabling ICMP make your product non-compliant to RFC1122, but
-    help to reduce footprint, and to reduce "visibility" on the Internet.
-
-  2007-09-05 Frédéric Bernon, Bill Florac
-  * opt.h, sys.h, tcpip.c, slipif.c, ppp.c, sys_arch.txt: Change parameters list
-    for sys_thread_new (see "task #7252 : Create sys_thread_new_ex()"). Two new
-    parameters have to be provided: a task name, and a task stack size. For this
-    one, since it's platform dependant, you could define the best one for you in
-    your lwipopts.h. For port maintainers, you can just add these new parameters
-    in your sys_arch.c file, and but it's not mandatory, use them in your OS
-    specific functions.
-
-  2007-09-05 Frédéric Bernon
-  * inet.c, autoip.c, msg_in.c, msg_out.c, init.c: Move some build time checkings
-    inside init.c for task #7142 "Sanity check user-configurable values".
-
-  2007-09-04 Frédéric Bernon, Bill Florac
-  * igmp.h, igmp.c, memp_std.h, memp.c, init.c, opt.h: Replace mem_malloc call by
-    memp_malloc, and use a new MEMP_NUM_IGMP_GROUP option (see opt.h to define the
-    value). It will avoid potential fragmentation problems, use a counter to know
-    how many times a group is used on an netif, and free it when all applications
-    leave it. MEMP_NUM_IGMP_GROUP got 8 as default value (and init.c got a sanity
-    check if LWIP_IGMP!=0).
-
-  2007-09-03 Frédéric Bernon
-  * igmp.h, igmp.c, sockets.c, api_msg.c: Changes for "#20503 IGMP Improvement".
-    Initialize igmp_mac_filter to NULL in netif_add (this field should be set in
-    the netif's "init" function). Use the "imr_interface" field (for socket layer)
-    and/or the "interface" field (for netconn layer), for join/leave operations.
-    The igmp_join/leavegroup first parameter change from a netif to an ipaddr.
-    This field could be a netif's ipaddr, or "any" (same meaning than ip_addr_isany).
-
-  2007-08-30 Frédéric Bernon
-  * Add netbuf.h, netbuf.c, Change api.h, api_lib.c: #7249 "Split netbuf functions
-    from api/api_lib". Now netbuf API is independant of netconn, and can be used
-    with other API (application based on raw API, or future "socket2" API). Ports
-    maintainers just have to add src/api/netbuf.c in their makefile/projects.
-
-  2007-08-30 Frédéric Bernon, Jonathan Larmour
-  * init.c: Add first version of lwip_sanity_check for task #7142 "Sanity check
-    user-configurable values".
-
-  2007-08-29 Frédéric Bernon
-  * igmp.h, igmp.c, tcpip.c, init.c, netif.c: change igmp_init and add igmp_start.
-    igmp_start is call inside netif_add. Now, igmp initialization is in the same
-    spirit than the others modules. Modify some IGMP debug traces.
-
-  2007-08-29 Frédéric Bernon
-  * Add init.h, init.c, Change opt.h, tcpip.c: Task  #7213 "Add a lwip_init function"
-    Add lwip_init function to regroup all modules initializations, and to provide
-    a place to add code for task #7142 "Sanity check user-configurable values".
-    Ports maintainers should remove direct initializations calls from their code,
-    and add init.c in their makefiles. Note that lwip_init() function is called
-    inside tcpip_init, but can also be used by raw api users since all calls are
-    disabled when matching options are disabled. Also note that their is new options
-    in opt.h, you should configure in your lwipopts.h (they are enabled per default).
-
-  2007-08-26 Marc Boucher
-  * api_msg.c: do_close_internal(): Reset the callbacks and arg (conn) to NULL
-    since they can under certain circumstances be called with an invalid conn
-    pointer after the connection has been closed (and conn has been freed). 
-
-  2007-08-25 Frédéric Bernon (Artem Migaev's Patch)
-  * netif.h, netif.c: Integrate "patch #6163 : Function to check if link layer is up".
-    Add a netif_is_link_up() function if LWIP_NETIF_LINK_CALLBACK option is set.
-
-  2007-08-22 Frédéric Bernon
-  * netif.h, netif.c, opt.h: Rename LWIP_NETIF_CALLBACK in LWIP_NETIF_STATUS_CALLBACK
-    to be coherent with new LWIP_NETIF_LINK_CALLBACK option before next release.
-
-  2007-08-22 Frédéric Bernon
-  * tcpip.h, tcpip.c, ethernetif.c, opt.h: remove options ETHARP_TCPIP_INPUT &
-    ETHARP_TCPIP_ETHINPUT, now, only "ethinput" code is supported, even if the 
-    name is tcpip_input (we keep the name of 1.2.0 function).
-
-  2007-08-17 Jared Grubb
-  * memp_std.h, memp.h, memp.c, mem.c, stats.c: (Task #7136) Centralize mempool 
-    settings into new memp_std.h and optional user file lwippools.h. This adds
-    more dynamic mempools, and allows the user to create an arbitrary number of
-    mempools for mem_malloc.
-
-  2007-08-16 Marc Boucher
-  * api_msg.c: Initialize newconn->state to NETCONN_NONE in accept_function;
-    otherwise it was left to NETCONN_CLOSE and sent_tcp() could prematurely
-    close the connection.
-
-  2007-08-16 Marc Boucher
-  * sockets.c: lwip_accept(): check netconn_peer() error return.
-
-  2007-08-16 Marc Boucher
-  * mem.c, mem.h: Added mem_calloc().
-
-  2007-08-16 Marc Boucher
-  * tcpip.c, tcpip.h memp.c, memp.h: Added distinct memp (MEMP_TCPIP_MSG_INPKT)
-    for input packets to prevent floods from consuming all of MEMP_TCPIP_MSG
-    and starving other message types.
-    Renamed MEMP_TCPIP_MSG to MEMP_TCPIP_MSG_API
-
-  2007-08-16 Marc Boucher
-  * pbuf.c, pbuf.h, etharp.c, tcp_in.c, sockets.c: Split pbuf flags in pbuf
-    type and flgs (later renamed to flags).
-    Use enum pbuf_flag as pbuf_type.  Renumber PBUF_FLAG_*.
-    Improved lwip_recvfrom().  TCP push now propagated.
-
-  2007-08-16 Marc Boucher
-  * ethernetif.c, contrib/ports/various: ethbroadcast now a shared global
-    provided by etharp.
-
-  2007-08-16 Marc Boucher
-  * ppp_oe.c ppp_oe.h, auth.c chap.c fsm.c lcp.c ppp.c ppp.h,
-    etharp.c ethernetif.c, etharp.h, opt.h tcpip.h, tcpip.c:
-    Added PPPoE support and various PPP improvements.
-
-  2007-07-25 Simon Goldschmidt
-  * api_lib.c, ip_frag.c, pbuf.c, api.h, pbuf.h: Introduced pbuf_copy_partial,
-    making netbuf_copy_partial use this function.
-
-  2007-07-25 Simon Goldschmidt
-  * tcp_in.c: Fix bug #20506: Slow start / initial congestion window starts with
-    2 * mss (instead of 1 * mss previously) to comply with some newer RFCs and
-    other stacks.
-
-  2007-07-13 Jared Grubb (integrated by Frédéric Bernon)
-  * opt.h, netif.h, netif.c, ethernetif.c: Add new configuration option to add
-    a link callback in the netif struct, and functions to handle it. Be carefull
-    for port maintainers to add the NETIF_FLAG_LINK_UP flag (like in ethernetif.c)
-    if you want to be sure to be compatible with future changes...
-
-  2007-06-30 Frédéric Bernon
-  * sockets.h, sockets.c: Implement MSG_PEEK flag for recv/recvfrom functions.
-
-  2007-06-21 Simon Goldschmidt
-  * etharp.h, etharp.c: Combined etharp_request with etharp_raw for both
-    LWIP_AUTOIP =0 and =1 to remove redundant code.
-
-  2007-06-21 Simon Goldschmidt
-  * mem.c, memp.c, mem.h, memp.h, opt.h: task #6863: Introduced the option
-    MEM_USE_POOLS to use 4 pools with different sized elements instead of a
-    heap. This both prevents memory fragmentation and gives a higher speed
-    at the cost of more memory consumption. Turned off by default.
-
-  2007-06-21 Simon Goldschmidt
-  * api_lib.c, api_msg.c, api.h, api_msg.h: Converted the length argument of
-    netconn_write (and therefore also api_msg_msg.msg.w.len) from u16_t into
-    int to be able to send a bigger buffer than 64K with one time (mainly
-    used from lwip_send).
-
-  2007-06-21 Simon Goldschmidt
-  * tcp.h, api_msg.c: Moved the nagle algorithm from netconn_write/do_write
-    into a define (tcp_output_nagle) in tcp.h to provide it to raw api users, too.
-
-  2007-06-21 Simon Goldschmidt
-  * api.h, api_lib.c, api_msg.c: Fixed bug #20021: Moved sendbuf-processing in
-    netconn_write from api_lib.c to api_msg.c to also prevent multiple context-
-    changes on low memory or empty send-buffer.
-
-  2007-06-18 Simon Goldschmidt
-  * etharp.c, etharp.h: Changed etharp to use a defined hardware address length
-    of 6 to avoid loading netif->hwaddr_len every time (since this file is only
-    used for ethernet and struct eth_addr already had a defined length of 6).
-
-  2007-06-17 Simon Goldschmidt
-  * sockets.c, sockets.h: Implemented socket options SO_NO_CHECK for UDP sockets
-    to disable UDP checksum generation on transmit.
-
-  2007-06-13 Frédéric Bernon, Simon Goldschmidt
-  * debug.h, api_msg.c: change LWIP_ERROR to use it to check errors like invalid
-    pointers or parameters, and let the possibility to redefined it in cc.h. Use
-    this macro to check "conn" parameter in api_msg.c functions.
-
-  2007-06-11 Simon Goldschmidt
-  * sockets.c, sockets.h: Added UDP lite support for sockets
-
-  2007-06-10 Simon Goldschmidt
-  * udp.h, opt.h, api_msg.c, ip.c, udp.c: Included switch LWIP_UDPLITE (enabled
-    by default) to switch off UDP-Lite support if not needed (reduces udp.c code
-    size)
-
-  2007-06-09 Dominik Spies (integrated by Frédéric Bernon)
-  * autoip.h, autoip.c, dhcp.h, dhcp.c, netif.h, netif.c, etharp.h, etharp.c, opt.h:
-    AutoIP implementation available for IPv4, with new options LWIP_AUTOIP and
-    LWIP_DHCP_AUTOIP_COOP if you want to cooperate with DHCP. Some tips to adapt
-    (see TODO mark in the source code).
-
-  2007-06-09 Simon Goldschmidt
-  * etharp.h, etharp.c, ethernetif.c: Modified order of parameters for
-    etharp_output() to match netif->output so etharp_output() can be used
-    directly as netif->output to save one function call.
-
-  2007-06-08 Simon Goldschmidt
-  * netif.h, ethernetif.c, slipif.c, loopif.c: Added define
-    NETIF_INIT_SNMP(netif, type, speed) to initialize per-netif snmp variables,
-    added initialization of those to ethernetif, slipif and loopif.
-
-  2007-05-18 Simon Goldschmidt
-  * opt.h, ip_frag.c, ip_frag.h, ip.c: Added option IP_FRAG_USES_STATIC_BUF
-    (defaulting to off for now) that can be set to 0 to send fragmented
-    packets by passing PBUF_REFs down the stack.
-
-  2007-05-23 Frédéric Bernon
-  * api_lib.c: Implement SO_RCVTIMEO for accept and recv on TCP
-    connections, such present in patch #5959.
-
-  2007-05-23 Frédéric Bernon
-  * api.h, api_lib.c, api_msg.c, sockets.c: group the different NETCONN_UDPxxx
-    code in only one part...
-
-  2007-05-18 Simon Goldschmidt
-  * opt.h, memp.h, memp.c: Added option MEMP_OVERFLOW_CHECK to check for memp
-    elements to overflow. This is achieved by adding some bytes before and after
-    each pool element (increasing their size, of course), filling them with a
-    prominent value and checking them on freeing the element.
-    Set it to 2 to also check every element in every pool each time memp_malloc()
-    or memp_free() is called (slower but more helpful).
-
-  2007-05-10 Simon Goldschmidt
-  * opt.h, memp.h, memp.c, pbuf.c (see task #6831): use a new memp pool for
-    PBUF_POOL pbufs instead of the old pool implementation in pbuf.c to reduce
-    code size.
-
-  2007-05-11 Frédéric Bernon
-  * sockets.c, api_lib.c, api_msg.h, api_msg.c, netifapi.h, netifapi.c, tcpip.c:
-    Include a function pointer instead of a table index in the message to reduce
-    footprint. Disable some part of lwip_send and lwip_sendto if some options are
-    not set (LWIP_TCP, LWIP_UDP, LWIP_RAW).
-
-  2007-05-10 Simon Goldschmidt
-  * *.h (except netif/ppp/*.h): Included patch #5448: include '#ifdef __cplusplus
-    \ extern "C" {' in all header files. Now you can write your application using
-    the lwIP stack in C++ and simply #include the core files. Note I have left
-    out the netif/ppp/*h header files for now, since I don't know which files are
-    included by applications and which are for internal use only.
-
-  2007-05-09 Simon Goldschmidt
-  * opt.h, *.c/*.h: Included patch #5920: Create define to override C-library
-    memcpy. 2 Defines are created: MEMCPY() for normal memcpy, SMEMCPY() for
-    situations where some compilers might inline the copy and save a function
-    call. Also replaced all calls to memcpy() with calls to (S)MEMCPY().
-
-  2007-05-08 Simon Goldschmidt
-  * mem.h: If MEM_LIBC_MALLOC==1, allow the defines (e.g. mem_malloc() -> malloc())
-    to be overriden in case the C-library malloc implementation is not protected
-    against concurrent access.
-
-  2007-05-04 Simon Goldschmidt (Atte Kojo)
-  * etharp.c: Introduced fast one-entry-cache to speed up ARP lookup when sending
-    multiple packets to the same host.
-
-  2007-05-04 Frédéric Bernon, Jonathan Larmour
-  * sockets.c, api.h, api_lib.c, api_msg.h, api_msg.c: Fix bug #19162 "lwip_sento: a possible
-    to corrupt remote addr/port connection state". Reduce problems "not enought memory" with
-    netbuf (if we receive lot of datagrams). Improve lwip_sendto (only one exchange between
-    sockets api and api_msg which run in tcpip_thread context). Add netconn_sento function.
-    Warning, if you directly access to "fromaddr" & "fromport" field from netbuf struct,
-    these fields are now renamed "addr" & "port".
-
-  2007-04-11 Jonathan Larmour
-  * sys.h, api_lib.c: Provide new sys_mbox_tryfetch function. Require ports to provide new
-    sys_arch_mbox_tryfetch function to get a message if one is there, otherwise return
-    with SYS_MBOX_EMPTY. sys_arch_mbox_tryfetch can be implemented as a function-like macro
-    by the port in sys_arch.h if desired.
-
-  2007-04-06 Frédéric Bernon, Simon Goldschmidt
-  * opt.h, tcpip.h, tcpip.c, netifapi.h, netifapi.c: New configuration option LWIP_NETIF_API
-    allow to use thread-safe functions to add/remove netif in list, and to start/stop dhcp
-    clients, using new functions from netifapi.h. Disable as default (no port change to do).
-
-  2007-04-05 Frédéric Bernon
-  * sockets.c: remplace ENOBUFS errors on alloc_socket by ENFILE to be more BSD compliant.
-
-  2007-04-04 Simon Goldschmidt
-  * arch.h, api_msg.c, dhcp.c, msg_in.c, sockets.c: Introduced #define LWIP_UNUSED_ARG(x)
-    use this for and architecture-independent form to tell the compiler you intentionally
-    are not using this variable. Can be overriden in cc.h.
-
-  2007-03-28 Frédéric Bernon
-  * opt.h, netif.h, dhcp.h, dhcp.c: New configuration option LWIP_NETIF_HOSTNAME allow to
-    define a hostname in netif struct (this is just a pointer, so, you can use a hardcoded
-    string, point on one of your's ethernetif field, or alloc a string you will free yourself).
-    It will be used by DHCP to register a client hostname, but can also be use when you call
-    snmp_set_sysname.
-
-  2007-03-28 Frédéric Bernon
-  * netif.h, netif.c: A new NETIF_FLAG_ETHARP flag is defined in netif.h, to allow to 
-    initialize a network interface's flag with. It tell this interface is an ethernet
-    device, and we can use ARP with it to do a "gratuitous ARP" (RFC 3220 "IP Mobility
-    Support for IPv4" section 4.6) when interface is "up" with netif_set_up().
-
-  2007-03-26 Frédéric Bernon, Jonathan Larmour
-  * opt.h, tcpip.c: New configuration option LWIP_ARP allow to disable ARP init at build
-    time if you only use PPP or SLIP. The default is enable. Note we don't have to call 
-    etharp_init in your port's initilization sequence if you use tcpip.c, because this call
-    is done in tcpip_init function.
-
-  2007-03-22 Frédéric Bernon
-  * stats.h, stats.c, msg_in.c: Stats counters can be change to u32_t if necessary with the
-    new option LWIP_STATS_LARGE. If you need this option, define LWIP_STATS_LARGE to 1 in
-    your lwipopts.h. More, unused counters are not defined in the stats structs, and not 
-    display by stats_display(). Note that some options (SYS_STATS and RAW_STATS) are defined
-    but never used. Fix msg_in.c with the correct #if test for a stat display.
-
-  2007-03-21 Kieran Mansley
-  * netif.c, netif.h: Apply patch#4197 with some changes (originator: rireland@hmgsl.com). 
-    Provides callback on netif up/down state change.
-
-  2007-03-11 Frédéric Bernon, Mace Gael, Steve Reynolds
-  * sockets.h, sockets.c, api.h, api_lib.c, api_msg.h, api_msg.c, igmp.h, igmp.c,
-    ip.c, netif.h, tcpip.c, opt.h:
-    New configuration option LWIP_IGMP to enable IGMP processing. Based on only one 
-    filter per all network interfaces. Declare a new function in netif to enable to
-    control the MAC filter (to reduce lwIP traffic processing).
-
-  2007-03-11 Frédéric Bernon
-  * tcp.h, tcp.c, sockets.c, tcp_out.c, tcp_in.c, opt.h: Keepalive values can
-    be configured at run time with LWIP_TCP_KEEPALIVE, but don't change this
-    unless you know what you're doing (default are RFC1122 compliant). Note
-    that TCP_KEEPIDLE and TCP_KEEPINTVL have to be set in seconds.
-
-  2007-03-08 Frédéric Bernon
-  * tcp.h: Keepalive values can be configured at compile time, but don't change
-    this unless you know what you're doing (default are RFC1122 compliant).
-
-  2007-03-08 Frédéric Bernon
-  * sockets.c, api.h, api_lib.c, tcpip.c, sys.h, sys.c, err.c, opt.h:
-    Implement LWIP_SO_RCVTIMEO configuration option to enable/disable SO_RCVTIMEO
-    on UDP sockets/netconn.
-
-  2007-03-08 Simon Goldschmidt
-  * snmp_msg.h, msg_in.c: SNMP UDP ports can be configured at compile time.
-
-  2007-03-06 Frédéric Bernon
-  * api.h, api_lib.c, sockets.h, sockets.c, tcpip.c, sys.h, sys.c, err.h: 
-    Implement SO_RCVTIMEO on UDP sockets/netconn.
-
-  2007-02-28 Kieran Mansley (based on patch from Simon Goldschmidt)
-  * api_lib.c, tcpip.c, memp.c, memp.h: make API msg structs allocated
-    on the stack and remove the API msg type from memp
-
-  2007-02-26 Jonathan Larmour (based on patch from Simon Goldschmidt)
-  * sockets.h, sockets.c: Move socket initialization to new
-    lwip_socket_init() function.
-    NOTE: this changes the API with ports. Ports will have to be
-    updated to call lwip_socket_init() now.
-
-  2007-02-26 Jonathan Larmour (based on patch from Simon Goldschmidt)
-  * api_lib.c: Use memcpy in netbuf_copy_partial.
-
-
-  ++ Bug fixes:
-
-  2008-03-17 Frédéric Bernon, Ed Kerekes
-  * igmp.h, igmp.c: Fix bug #22613 "IGMP iphdr problem" (could have
-    some problems to fill the IP header on some targets, use now the
-    ip.h macros to do it).
-
-  2008-03-13 Frédéric Bernon
-  * sockets.c: Fix bug #22435 "lwip_recvfrom with TCP break;". Using
-    (lwip_)recvfrom with valid "from" and "fromlen" parameters, on a
-    TCP connection caused a crash. Note that using (lwip_)recvfrom
-    like this is a bit slow and that using (lwip)getpeername is the
-    good lwip way to do it (so, using recv is faster on tcp sockets).
-
-  2008-03-12 Frédéric Bernon, Jonathan Larmour
-  * api_msg.c, contrib/apps/ping.c: Fix bug #22530 "api_msg.c's
-    recv_raw() does not consume data", and the ping sample (with
-    LWIP_SOCKET=1, the code did the wrong supposition that lwip_recvfrom
-    returned the IP payload, without the IP header).
-
-  2008-03-04 Jonathan Larmour
-  * mem.c, stats.c, mem.h: apply patch #6414 to avoid compiler errors
-  and/or warnings on some systems where mem_size_t and size_t differ.
-  * pbuf.c, ppp.c: Fix warnings on some systems with mem_malloc.
-
-  2008-03-04 Kieran Mansley (contributions by others) 
-  * Numerous small compiler error/warning fixes from contributions to
-    mailing list after 1.3.0 release candidate made.
-
-  2008-01-25 Cui hengbin (integrated by Frédéric Bernon)
-  * dns.c: Fix bug #22108 "DNS problem" caused by unaligned structures.
-
-  2008-01-15 Kieran Mansley
-  * tcp_out.c: BUG20511.  Modify persist timer to start when we are
-    prevented from sending by a small send window, not just a zero
-    send window.
-
-  2008-01-09 Jonathan Larmour
-  * opt.h, ip.c: Rename IP_OPTIONS define to IP_OPTIONS_ALLOWED to avoid
-    conflict with Linux system headers.
-
-  2008-01-06 Jonathan Larmour
-  * dhcp.c: fix bug #19927: "DHCP NACK problem" by clearing any existing set IP
-    address entirely on receiving a DHCPNAK, and restarting discovery.
-
-  2007-12-21 Simon Goldschmidt
-  * sys.h, api_lib.c, api_msg.c, sockets.c: fix bug #21698: "netconn->recv_avail
-    is not protected" by using new macros for interlocked access to modify/test
-    netconn->recv_avail.
-
-  2007-12-20 Kieran Mansley (based on patch from Oleg Tyshev)
-  * tcp_in.c: fix bug# 21535 (nrtx not reset correctly in SYN_SENT state)
-
-  2007-12-20 Kieran Mansley (based on patch from Per-Henrik Lundbolm)
-  * tcp.c, tcp_in.c, tcp_out.c, tcp.h: fix bug #20199 (better handling
-    of silly window avoidance and prevent lwIP from shrinking the window)
-
-  2007-12-04 Simon Goldschmidt
-  * tcp.c, tcp_in.c: fix bug #21699 (segment leak in ooseq processing when last
-    data packet was lost): add assert that all segment lists are empty in
-    tcp_pcb_remove before setting pcb to CLOSED state; don't directly set CLOSED
-    state from LAST_ACK in tcp_process
-
-  2007-12-02 Simon Goldschmidt
-  * sockets.h: fix bug #21654: exclude definition of struct timeval from #ifndef FD_SET
-    If including <sys/time.h> for system-struct timeval, LWIP_TIMEVAL_PRIVATE now
-    has to be set to 0 in lwipopts.h
-
-  2007-12-02 Simon Goldschmidt
-  * api_msg.c, api_lib.c: fix bug #21656 (recvmbox problem in netconn API): always
-    allocate a recvmbox in netconn_new_with_proto_and_callback. For a tcp-listen
-    netconn, this recvmbox is later freed and a new mbox is allocated for acceptmbox.
-    This is a fix for thread-safety and allocates all items needed for a netconn
-    when the netconn is created.
-
-  2007-11-30 Simon Goldschmidt
-  * udp.c: first attempt to fix bug #21655 (DHCP doesn't work reliably with multiple
-    netifs): if LWIP_DHCP is enabled, UDP packets to DHCP_CLIENT_PORT are passed
-    to netif->dhcp->pcb only (if that exists) and not to any other pcb for the same
-    port (only solution to let UDP pcbs 'bind' to a netif instead of an IP address)
-
-  2007-11-27 Simon Goldschmidt
-  * ip.c: fixed bug #21643 (udp_send/raw_send don't fail if netif is down) by
-    letting ip_route only use netifs that are up.
-
-  2007-11-27 Simon Goldschmidt
-  * err.h, api_lib.c, api_msg.c, sockets.c: Changed error handling: ERR_MEM, ERR_BUF
-    and ERR_RTE are seen as non-fatal, all other errors are fatal. netconns and
-    sockets block most operations once they have seen a fatal error.
-
-  2007-11-27 Simon Goldschmidt
-  * udp.h, udp.c, dhcp.c: Implemented new function udp_sendto_if which takes the
-    netif to send as an argument (to be able to send on netifs that are down).
-
-  2007-11-26 Simon Goldschmidt
-  * tcp_in.c: Fixed bug #21582: pcb->acked accounting can be wrong when ACKs
-    arrive out-of-order
-
-  2007-11-21 Simon Goldschmidt
-  * tcp.h, tcp_out.c, api_msg.c: Fixed bug #20287: tcp_output_nagle sends too early
-    Fixed the nagle algorithm; nagle now also works for all raw API applications
-    and has to be explicitly disabled with 'tcp_pcb->flags |= TF_NODELAY'
-
-  2007-11-12 Frédéric Bernon
-  * sockets.c, api.h, api_lib.c, api_msg.h, api_msg.c: Fixed bug #20900. Now, most
-    of the netconn_peer and netconn_addr processing is done inside tcpip_thread
-    context in do_getaddr.
-
-  2007-11-10 Simon Goldschmidt
-  * etharp.c: Fixed bug: assert fired when MEMP_ARP_QUEUE was empty (which can
-    happen any time). Now the packet simply isn't enqueued when out of memory.
-
-  2007-11-01 Simon Goldschmidt
-  * tcp.c, tcp_in.c: Fixed bug #21494: The send mss (pcb->mss) is set to 536 (or
-    TCP_MSS if that is smaller) as long as no MSS option is received from the
-    remote host.
-
-  2007-11-01 Simon Goldschmidt
-  * tcp.h, tcp.c, tcp_in.c: Fixed bug #21491: The MSS option sent (with SYN)
-    is now based on TCP_MSS instead of pcb->mss (on passive open now effectively
-    sending our configured TCP_MSS instead of the one received).
-
-  2007-11-01 Simon Goldschmidt
-  * tcp_in.c: Fixed bug #21181: On active open, the initial congestion window was
-    calculated based on the configured TCP_MSS, not on the MSS option received
-    with SYN+ACK.
-
-  2007-10-09 Simon Goldschmidt
-  * udp.c, inet.c, inet.h: Fixed UDPLite: send: Checksum was always generated too
-    short and also was generated wrong if checksum coverage != tot_len;
-    receive: checksum was calculated wrong if checksum coverage != tot_len
-
-  2007-10-08 Simon Goldschmidt
-  * mem.c: lfree was not updated in mem_realloc!
-
-  2007-10-07 Frédéric Bernon
-  * sockets.c, api.h, api_lib.c: First step to fix "bug #20900 : Potential
-    crash error problem with netconn_peer & netconn_addr". VERY IMPORTANT:
-    this change cause an API breakage for netconn_addr, since a parameter
-    type change. Any compiler should cause an error without any changes in
-    yours netconn_peer calls (so, it can't be a "silent change"). It also
-    reduce a little bit the footprint for socket layer (lwip_getpeername &
-    lwip_getsockname use now a common lwip_getaddrname function since 
-    netconn_peer & netconn_addr have the same parameters).
-
-  2007-09-20 Simon Goldschmidt
-  * tcp.c: Fixed bug #21080 (tcp_bind without check pcbs in TIME_WAIT state)
-    by checking  tcp_tw_pcbs also
-
-  2007-09-19 Simon Goldschmidt
-  * icmp.c: Fixed bug #21107 (didn't reset IP TTL in ICMP echo replies)
-
-  2007-09-15 Mike Kleshov
-  * mem.c: Fixed bug #21077 (inaccuracy in calculation of lwip_stat.mem.used)
-
-  2007-09-06 Frédéric Bernon
-  * several-files: replace some #include "arch/cc.h" by "lwip/arch.h", or simply remove
-    it as long as "lwip/opt.h" is included before (this one include "lwip/debug.h" which
-    already include "lwip/arch.h"). Like that, default defines are provided by "lwip/arch.h"
-    if they are not defined in cc.h, in the same spirit than "lwip/opt.h" for lwipopts.h.
-
-  2007-08-30 Frédéric Bernon
-  * igmp.h, igmp.c: Some changes to remove some redundant code, add some traces, 
-    and fix some coding style.
-
-  2007-08-28 Frédéric Bernon
-  * tcpip.c: Fix TCPIP_MSG_INPKT processing: now, tcpip_input can be used for any
-    kind of packets. These packets are considered like Ethernet packets (payload 
-    pointing to ethhdr) if the netif got the NETIF_FLAG_ETHARP flag. Else, packets 
-    are considered like IP packets (payload pointing to iphdr).
-
-  2007-08-27 Frédéric Bernon
-  * api.h, api_lib.c, api_msg.c: First fix for "bug #20900 : Potential crash error
-    problem with netconn_peer & netconn_addr". Introduce NETCONN_LISTEN netconn_state
-    and remove obsolete ones (NETCONN_RECV & NETCONN_ACCEPT).
-
-  2007-08-24 Kieran Mansley
-  * inet.c Modify (acc >> 16) test to ((acc >> 16) != 0) to help buggy
-    compiler (Paradigm C++)
-
-  2007-08-09 Frédéric Bernon, Bill Florac
-  * stats.h, stats.c, igmp.h, igmp.c, opt.h: Fix for bug #20503 : IGMP Improvement.
-    Introduce IGMP_STATS to centralize statistics management.
-
-  2007-08-09 Frédéric Bernon, Bill Florac
-  * udp.c: Fix for bug #20503 : IGMP Improvement. Enable to receive a multicast
-    packet on a udp pcb binded on an netif's IP address, and not on "any".
-
-  2007-08-09 Frédéric Bernon, Bill Florac
-  * igmp.h, igmp.c, ip.c: Fix minor changes from bug #20503 : IGMP Improvement.
-    This is mainly on using lookup/lookfor, and some coding styles...
-
-  2007-07-26 Frédéric Bernon (and "thedoctor")
-  * igmp.c: Fix bug #20595 to accept IGMPv3 "Query" messages.
-
-  2007-07-25 Simon Goldschmidt
-  * api_msg.c, tcp.c: Another fix for bug #20021: by not returning an error if
-    tcp_output fails in tcp_close, the code in do_close_internal gets simpler
-    (tcp_output is called again later from tcp timers).
-
-  2007-07-25 Simon Goldschmidt
-  * ip_frag.c: Fixed bug #20429: use the new pbuf_copy_partial instead of the old
-    copy_from_pbuf, which illegally modified the given pbuf.
-
-  2007-07-25 Simon Goldschmidt
-  * tcp_out.c: tcp_enqueue: pcb->snd_queuelen didn't work for chaine PBUF_RAMs:
-    changed snd_queuelen++ to snd_queuelen += pbuf_clen(p).
-
-  2007-07-24 Simon Goldschmidt
-  * api_msg.c, tcp.c: Fix bug #20480: Check the pcb passed to tcp_listen() for the
-    correct state (must be CLOSED).
-
-  2007-07-13 Thomas Taranowski (commited by Jared Grubb)
-  * memp.c: Fix bug #20478: memp_malloc returned NULL+MEMP_SIZE on failed
-    allocation. It now returns NULL.
-
-  2007-07-13 Frédéric Bernon
-  * api_msg.c: Fix bug #20318: api_msg "recv" callbacks don't call pbuf_free in
-    all error cases.
-
-  2007-07-13 Frédéric Bernon
-  * api_msg.c: Fix bug #20315: possible memory leak problem if tcp_listen failed,
-    because current code doesn't follow rawapi.txt documentation.
-
-  2007-07-13 Kieran Mansley
-  * src/core/tcp_in.c Apply patch#5741 from Oleg Tyshev to fix bug in
-    out of sequence processing of received packets
-
-  2007-07-03 Simon Goldschmidt
-  * nearly-all-files: Added assertions where PBUF_RAM pbufs are used and an
-    assumption is made that this pbuf is in one piece (i.e. not chained). These
-    assumptions clash with the possibility of converting to fully pool-based
-    pbuf implementations, where PBUF_RAM pbufs might be chained.
-
-  2007-07-03 Simon Goldschmidt
-  * api.h, api_lib.c, api_msg.c: Final fix for bug #20021 and some other problems
-    when closing tcp netconns: removed conn->sem, less context switches when
-    closing, both netconn_close and netconn_delete should safely close tcp
-    connections.
-
-  2007-07-02 Simon Goldschmidt
-  * ipv4/ip.h, ipv6/ip.h, opt.h, netif.h, etharp.h, ipv4/ip.c, netif.c, raw.c,
-    tcp_out.c, udp.c, etharp.c: Added option LWIP_NETIF_HWADDRHINT (default=off)
-    to cache ARP table indices with each pcb instead of single-entry cache for
-    the complete stack.
-
-  2007-07-02 Simon Goldschmidt
-  * tcp.h, tcp.c, tcp_in.c, tcp_out.c: Added some ASSERTS and casts to prevent
-    warnings when assigning to smaller types.
-
-  2007-06-28 Simon Goldschmidt
-  * tcp_out.c: Added check to prevent tcp_pcb->snd_queuelen from overflowing.
-
-  2007-06-28 Simon Goldschmidt
-  * tcp.h: Fixed bug #20287: Fixed nagle algorithm (sending was done too early if
-    a segment contained chained pbufs)
-
-  2007-06-28 Frédéric Bernon
-  * autoip.c: replace most of rand() calls by a macro LWIP_AUTOIP_RAND which compute
-    a "pseudo-random" value based on netif's MAC and some autoip fields. It's always
-    possible to define this macro in your own lwipopts.h to always use C library's
-    rand(). Note that autoip_create_rand_addr doesn't use this macro.
-
-  2007-06-28 Frédéric Bernon
-  * netifapi.h, netifapi.c, tcpip.h, tcpip.c: Update code to handle the option
-    LWIP_TCPIP_CORE_LOCKING, and do some changes to be coherent with last modifications
-    in api_lib/api_msg (use pointers and not type with table, etc...) 
-
-  2007-06-26 Simon Goldschmidt
-  * udp.h: Fixed bug #20259: struct udp_hdr was lacking the packin defines.
-
-  2007-06-25 Simon Goldschmidt
-  * udp.c: Fixed bug #20253: icmp_dest_unreach was called with a wrong p->payload
-    for udp packets with no matching pcb.
-
-  2007-06-25 Simon Goldschmidt
-  * udp.c: Fixed bug #20220: UDP PCB search in udp_input(): a non-local match
-    could get udp input packets if the remote side matched.
-
-  2007-06-13 Simon Goldschmidt
-  * netif.c: Fixed bug #20180 (TCP pcbs listening on IP_ADDR_ANY could get
-    changed in netif_set_ipaddr if previous netif->ip_addr.addr was 0.
-
-  2007-06-13 Simon Goldschmidt
-  * api_msg.c: pcb_new sets conn->err if protocol is not implemented
-    -> netconn_new_..() does not allocate a new connection for unsupported
-    protocols.
-
-  2007-06-13 Frédéric Bernon, Simon Goldschmidt
-  * api_lib.c: change return expression in netconn_addr and netconn_peer, because
-    conn->err was reset to ERR_OK without any reasons (and error was lost)...
-
-  2007-06-13 Frédéric Bernon, Matthias Weisser
-  * opt.h, mem.h, mem.c, memp.c, pbuf.c, ip_frag.c, vj.c: Fix bug #20162. Rename
-    MEM_ALIGN in LWIP_MEM_ALIGN and MEM_ALIGN_SIZE in LWIP_MEM_ALIGN_SIZE to avoid
-    some macro names collision with some OS macros.
-
-  2007-06-11 Simon Goldschmidt
-  * udp.c: UDP Lite: corrected the use of chksum_len (based on RFC3828: if it's 0,
-    create checksum over the complete packet. On RX, if it's < 8 (and not 0),
-    discard the packet. Also removed the duplicate 'udphdr->chksum = 0' for both
-    UDP & UDP Lite.
-
-  2007-06-11 Srinivas Gollakota & Oleg Tyshev
-  * tcp_out.c: Fix for bug #20075 : "A problem with keep-alive timer and TCP flags"
-    where TCP flags wasn't initialized in tcp_keepalive.
-
-  2007-06-03 Simon Goldschmidt
-  * udp.c: udp_input(): Input pbuf was not freed if pcb had no recv function
-    registered, p->payload was modified without modifying p->len if sending
-    icmp_dest_unreach() (had no negative effect but was definitively wrong).
-
-  2007-06-03 Simon Goldschmidt
-  * icmp.c: Corrected bug #19937: For responding to an icmp echo request, icmp
-    re-used the input pbuf even if that didn't have enough space to include the
-    link headers. Now the space is tested and a new pbuf is allocated for the
-    echo response packet if the echo request pbuf isn't big enough.
-
-  2007-06-01 Simon Goldschmidt
-  * sockets.c: Checked in patch #5914: Moved sockopt processing into tcpip_thread.
-
-  2007-05-23 Frédéric Bernon
-  * api_lib.c, sockets.c: Fixed bug #5958 for netconn_listen (acceptmbox only
-    allocated by do_listen if success) and netconn_accept errors handling. In
-    most of api_lib functions, we replace some errors checkings like "if (conn==NULL)"
-    by ASSERT, except for netconn_delete.
-
-  2007-05-23 Frédéric Bernon
-  * api_lib.c: Fixed bug #5957 "Safe-thread problem inside netconn_recv" to return
-    an error code if it's impossible to fetch a pbuf on a TCP connection (and not
-    directly close the recvmbox).
-
-  2007-05-22 Simon Goldschmidt
-  * tcp.c: Fixed bug #1895 (tcp_bind not correct) by introducing a list of
-    bound but unconnected (and non-listening) tcp_pcbs.
-
-  2007-05-22 Frédéric Bernon
-  * sys.h, sys.c, api_lib.c, tcpip.c: remove sys_mbox_fetch_timeout() (was only
-    used for LWIP_SO_RCVTIMEO option) and use sys_arch_mbox_fetch() instead of
-    sys_mbox_fetch() in api files. Now, users SHOULD NOT use internal lwIP features
-    like "sys_timeout" in their application threads.
-
-  2007-05-22 Frédéric Bernon
-  * api.h, api_lib.c, api_msg.h, api_msg.c: change the struct api_msg_msg to see
-    which parameters are used by which do_xxx function, and to avoid "misusing"
-    parameters (patch #5938).
-
-  2007-05-22 Simon Goldschmidt
-  * api_lib.c, api_msg.c, raw.c, api.h, api_msg.h, raw.h: Included patch #5938:
-    changed raw_pcb.protocol from u16_t to u8_t since for IPv4 and IPv6, proto
-    is only 8 bits wide. This affects the api, as there, the protocol was
-    u16_t, too.
-
-  2007-05-18 Simon Goldschmidt
-  * memp.c: addition to patch #5913: smaller pointer was returned but
-    memp_memory was the same size -> did not save memory.
-
-  2007-05-16 Simon Goldschmidt
-  * loopif.c, slipif.c: Fix bug #19729: free pbuf if netif->input() returns
-    != ERR_OK.
-
-  2007-05-16 Simon Goldschmidt
-  * api_msg.c, udp.c: If a udp_pcb has a local_ip set, check if it is the same
-    as the one of the netif used for sending to prevent sending from old
-    addresses after a netif address gets changed (partly fixes bug #3168).
-
-  2007-05-16 Frédéric Bernon
-  * tcpip.c, igmp.h, igmp.c: Fixed bug "#19800 : IGMP: igmp_tick() will not work
-    with NO_SYS=1". Note that igmp_init is always in tcpip_thread (and not in 
-    tcpip_init) because we have to be sure that network interfaces are already
-    added (mac filter is updated only in igmp_init for the moment).
-
-  2007-05-16 Simon Goldschmidt
-  * mem.c, memp.c: Removed semaphores from memp, changed sys_sem_wait calls
-    into sys_arch_sem_wait calls to prevent timers from running while waiting
-    for the heap. This fixes bug #19167.
-
-  2007-05-13 Simon Goldschmidt
-  * tcp.h, sockets.h, sockets.c: Fixed bug from patch #5865 by moving the defines
-    for socket options (lwip_set/-getsockopt) used with level IPPROTO_TCP from
-    tcp.h to sockets.h.
-
-  2007-05-07 Simon Goldschmidt
-  * mem.c: Another attempt to fix bug #17922.
-
-  2007-05-04 Simon Goldschmidt
-  * pbuf.c, pbuf.h, etharp.c: Further update to ARP queueing: Changed pbuf_copy()
-    implementation so that it can be reused (don't allocate the target
-    pbuf inside pbuf_copy()).
-
-  2007-05-04 Simon Goldschmidt
-  * memp.c: checked in patch #5913: in memp_malloc() we can return memp as mem
-    to save a little RAM (next pointer of memp is not used while not in pool).
-
-  2007-05-03 "maq"
-  * sockets.c: Fix ioctl FIONREAD when some data remains from last recv.
-    (patch #3574).
-
-  2007-04-23 Simon Goldschmidt
-  * loopif.c, loopif.h, opt.h, src/netif/FILES: fix bug #2595: "loopif results
-    in NULL reference for incoming TCP packets". Loopif has to be configured
-    (using LWIP_LOOPIF_MULTITHREADING) to directly call netif->input()
-    (multithreading environments, e.g. netif->input() = tcpip_input()) or
-    putting packets on a list that is fed to the stack by calling loopif_poll()
-    (single-thread / NO_SYS / polling environment where e.g.
-    netif->input() = ip_input).
-
-  2007-04-17 Jonathan Larmour
-  * pbuf.c: Use s32_t in pbuf_realloc(), as an s16_t can't reliably hold
-    the difference between two u16_t's.
-  * sockets.h: FD_SETSIZE needs to match number of sockets, which is
-    MEMP_NUM_NETCONN in sockets.c right now.
-
-  2007-04-12 Jonathan Larmour
-  * icmp.c: Reset IP header TTL in ICMP ECHO responses (bug #19580).
-
-  2007-04-12 Kieran Mansley
-  * tcp.c, tcp_in.c, tcp_out.c, tcp.h: Modify way the retransmission
-    timer is reset to fix bug#19434, with help from Oleg Tyshev.
-
-  2007-04-11 Simon Goldschmidt
-  * etharp.c, pbuf.c, pbuf.h: 3rd fix for bug #11400 (arp-queuing): More pbufs than
-    previously thought need to be copied (everything but PBUF_ROM!). Cleaned up
-    pbuf.c: removed functions no needed any more (by etharp).
-
-  2007-04-11 Kieran Mansley
-  * inet.c, ip_addr.h, sockets.h, sys.h, tcp.h: Apply patch #5745: Fix
-    "Constant is long" warnings with 16bit compilers.  Contributed by
-    avatar@mmlab.cse.yzu.edu.tw
-
-  2007-04-05 Frédéric Bernon, Jonathan Larmour
-  * api_msg.c: Fix bug #16830: "err_tcp() posts to connection mailbox when no pend on
-    the mailbox is active". Now, the post is only done during a connect, and do_send,
-    do_write and do_join_leave_group don't do anything if a previous error was signaled.
-
-  2007-04-03 Frédéric Bernon
-  * ip.c: Don't set the IP_DF ("Don't fragment") flag in the IP header in IP output
-    packets. See patch #5834.
-
-  2007-03-30 Frédéric Bernon
-  * api_msg.c: add a "pcb_new" helper function to avoid redundant code, and to add
-    missing  pcb allocations checking (in do_bind, and for each raw_new). Fix style.
-
-  2007-03-30 Frédéric Bernon
-  * most of files: prefix all debug.h define with "LWIP_" to avoid any conflict with
-    others environment defines (these were too "generic").
-
-  2007-03-28 Frédéric Bernon
-  * api.h, api_lib.c, sockets.c: netbuf_ref doesn't check its internal pbuf_alloc call
-    result and can cause a crash. lwip_send now check netbuf_ref result.
-
-  2007-03-28 Simon Goldschmidt
-  * sockets.c Remove "#include <errno.h>" from sockets.c to avoid multiple
-    definition of macros (in errno.h and lwip/arch.h) if LWIP_PROVIDE_ERRNO is
-    defined. This is the way it should have been already (looking at
-    doc/sys_arch.txt)
-
-  2007-03-28 Kieran Mansley
-  * opt.h Change default PBUF_POOL_BUFSIZE (again) to accomodate default MSS +
-    IP and TCP headers *and* physical link headers
-
-  2007-03-26 Frédéric Bernon (based on patch from Dmitry Potapov)
-  * api_lib.c: patch for netconn_write(), fixes a possible race condition which cause
-    to send some garbage. It is not a definitive solution, but the patch does solve
-    the problem for most cases.
-
-  2007-03-22 Frédéric Bernon
-  * api_msg.h, api_msg.c: Remove obsolete API_MSG_ACCEPT and do_accept (never used).
-
-  2007-03-22 Frédéric Bernon
-  * api_lib.c: somes resources couldn't be freed if there was errors during
-    netconn_new_with_proto_and_callback.
-
-  2007-03-22 Frédéric Bernon
-  * ethernetif.c: update netif->input calls to check return value. In older ports,
-    it's a good idea to upgrade them, even if before, there could be another problem
-    (access to an uninitialized mailbox).
-
-  2007-03-21 Simon Goldschmidt
-  * sockets.c: fixed bug #5067 (essentialy a signed/unsigned warning fixed
-    by casting to unsigned).
-
-  2007-03-21 Frédéric Bernon
-  * api_lib.c, api_msg.c, tcpip.c: integrate sys_mbox_fetch(conn->mbox, NULL) calls from
-    api_lib.c to tcpip.c's tcpip_apimsg(). Now, use a local variable and not a
-    dynamic one from memp to send tcpip_msg to tcpip_thread in a synchrone call.
-    Free tcpip_msg from tcpip_apimsg is not done in tcpip_thread. This give a
-    faster and more reliable communication between api_lib and tcpip.
-
-  2007-03-21 Frédéric Bernon
-  * opt.h: Add LWIP_NETIF_CALLBACK (to avoid compiler warning) and set it to 0.
-
-  2007-03-21 Frédéric Bernon
-  * api_msg.c, igmp.c, igmp.h: Fix C++ style comments
-
-  2007-03-21 Kieran Mansley
-  * opt.h Change default PBUF_POOL_BUFSIZE to accomodate default MSS +
-    IP and TCP headers
-
-  2007-03-21 Kieran Mansley
-  * Fix all uses of pbuf_header to check the return value.  In some
-    cases just assert if it fails as I'm not sure how to fix them, but
-    this is no worse than before when they would carry on regardless
-    of the failure.
-
-  2007-03-21 Kieran Mansley
-  * sockets.c, igmp.c, igmp.h, memp.h: Fix C++ style comments and
-    comment out missing header include in icmp.c
-
-  2007-03-20 Frédéric Bernon
-  * memp.h, stats.c: Fix stats_display function where memp_names table wasn't
-    synchronized with memp.h.
-
-  2007-03-20 Frédéric Bernon
-  * tcpip.c: Initialize tcpip's mbox, and verify if initialized in tcpip_input,
-    tcpip_ethinput, tcpip_callback, tcpip_apimsg, to fix a init problem with 
-    network interfaces. Also fix a compiler warning.
-
-  2007-03-20 Kieran Mansley
-  * udp.c: Only try and use pbuf_header() to make space for headers if
-    not a ROM or REF pbuf.
-
-  2007-03-19 Frédéric Bernon
-  * api_msg.h, api_msg.c, tcpip.h, tcpip.c: Add return types to tcpip_apimsg()
-    and api_msg_post().
-
-  2007-03-19 Frédéric Bernon
-  * Remove unimplemented "memp_realloc" function from memp.h.
-
-  2007-03-11 Simon Goldschmidt
-  * pbuf.c: checked in patch #5796: pbuf_alloc: len field claculation caused
-    memory corruption.
-
-  2007-03-11 Simon Goldschmidt (based on patch from Dmitry Potapov)
-  * api_lib.c, sockets.c, api.h, api_msg.h, sockets.h: Fixed bug #19251
-    (missing `const' qualifier in socket functions), to get more compatible to
-    standard POSIX sockets.
-
-  2007-03-11 Frédéric Bernon (based on patch from Dmitry Potapov)
-  * sockets.c: Add asserts inside bind, connect and sendto to check input
-    parameters. Remove excessive set_errno() calls after get_socket(), because
-    errno is set inside of get_socket(). Move last sock_set_errno() inside
-    lwip_close.
-
-  2007-03-09 Simon Goldschmidt
-  * memp.c: Fixed bug #11400: New etharp queueing introduced bug: memp_memory
-    was allocated too small.
-
-  2007-03-06 Simon Goldschmidt
-  * tcpip.c: Initialize dhcp timers in tcpip_thread (if LWIP_DHCP) to protect
-    the stack from concurrent access.
-
-  2007-03-06 Frédéric Bernon, Dmitry Potapov
-  * tcpip.c, ip_frag.c, ethernetif.c: Fix some build problems, and a redundancy
-    call to "lwip_stats.link.recv++;" in low_level_input() & ethernetif_input().
-
-  2007-03-06 Simon Goldschmidt
-  * ip_frag.c, ip_frag.h: Reduce code size: don't include code in those files
-    if IP_FRAG == 0 and IP_REASSEMBLY == 0
-
-  2007-03-06 Frédéric Bernon, Simon Goldschmidt
-  * opt.h, ip_frag.h, tcpip.h, tcpip.c, ethernetif.c: add new configuration
-    option named ETHARP_TCPIP_ETHINPUT, which enable the new tcpip_ethinput.
-    Allow to do ARP processing for incoming packets inside tcpip_thread
-    (protecting ARP layer against concurrent access). You can also disable
-    old code using tcp_input with new define ETHARP_TCPIP_INPUT set to 0.
-    Older ports have to use tcpip_ethinput.
-
-  2007-03-06 Simon Goldschmidt (based on patch from Dmitry Potapov)
-  * err.h, err.c: fixed compiler warning "initialization dircards qualifiers
-    from pointer target type"
-
-  2007-03-05 Frédéric Bernon
-  * opt.h, sockets.h: add new configuration options (LWIP_POSIX_SOCKETS_IO_NAMES,
-    ETHARP_TRUST_IP_MAC, review SO_REUSE)
-
-  2007-03-04 Frédéric Bernon
-  * api_msg.c: Remove some compiler warnings : parameter "pcb" was never
-    referenced.
-
-  2007-03-04 Frédéric Bernon
-  * api_lib.c: Fix "[patch #5764] api_lib.c cleanup: after patch #5687" (from
-    Dmitry Potapov).
-    The api_msg struct stay on the stack (not moved to netconn struct).
-
-  2007-03-04 Simon Goldschmidt (based on patch from Dmitry Potapov)
-  * pbuf.c: Fix BUG#19168 - pbuf_free can cause deadlock (if
-    SYS_LIGHTWEIGHT_PROT=1 & freeing PBUF_RAM when mem_sem is not available)
-    Also fixed cast warning in pbuf_alloc()
-
-  2007-03-04 Simon Goldschmidt
-  * etharp.c, etharp.h, memp.c, memp.h, opt.h: Fix BUG#11400 - don't corrupt
-    existing pbuf chain when enqueuing multiple pbufs to a pending ARP request
-
-  2007-03-03 Frédéric Bernon
-  * udp.c: remove obsolete line "static struct udp_pcb *pcb_cache = NULL;"
-    It is static, and never used in udp.c except udp_init().
-
-  2007-03-02 Simon Goldschmidt
-  * tcpip.c: Moved call to ip_init(), udp_init() and tcp_init() from
-    tcpip_thread() to tcpip_init(). This way, raw API connections can be
-    initialized before tcpip_thread is running (e.g. before OS is started)
-
-  2007-03-02 Frédéric Bernon
-  * rawapi.txt: Fix documentation mismatch with etharp.h about etharp_tmr's call
-    interval.
-
-  2007-02-28 Kieran Mansley 
-  * pbuf.c: Fix BUG#17645 - ensure pbuf payload pointer is not moved
-    outside the region of the pbuf by pbuf_header()
-
-  2007-02-28 Kieran Mansley 
-  * sockets.c: Fix BUG#19161 - ensure milliseconds timeout is non-zero
-    when supplied timeout is also non-zero 
-
-(STABLE-1.2.0)
-
-  2006-12-05 Leon Woestenberg
-  * CHANGELOG: Mention STABLE-1.2.0 release.
-
-  ++ New features:
-
-  2006-12-01 Christiaan Simons
-  * mem.h, opt.h: Added MEM_LIBC_MALLOC option.
-    Note this is a workaround. Currently I have no other options left.
-
-  2006-10-26 Christiaan Simons (accepted patch by Jonathan Larmour)
-  * ipv4/ip_frag.c: rename MAX_MTU to IP_FRAG_MAX_MTU and move define
-    to include/lwip/opt.h.
-  * ipv4/lwip/ip_frag.h: Remove unused IP_REASS_INTERVAL.
-    Move IP_REASS_MAXAGE and IP_REASS_BUFSIZE to include/lwip/opt.h.
-  * opt.h: Add above new options.
-
-  2006-08-18 Christiaan Simons
-  * tcp_{in,out}.c: added SNMP counters.
-  * ipv4/ip.c: added SNMP counters.
-  * ipv4/ip_frag.c: added SNMP counters.
-
-  2006-08-08 Christiaan Simons
-  * etharp.{c,h}: added etharp_find_addr() to read
-    (stable) ethernet/IP address pair from ARP table
-
-  2006-07-14 Christiaan Simons
-  * mib_structs.c: added
-  * include/lwip/snmp_structs.h: added
-  * netif.{c,h}, netif/ethernetif.c: added SNMP statistics to netif struct
-
-  2006-07-06 Christiaan Simons
-  * snmp/asn1_{enc,dec}.c added
-  * snmp/mib2.c added
-  * snmp/msg_{in,out}.c added
-  * include/lwip/snmp_asn1.h added
-  * include/lwip/snmp_msg.h added
-  * doc/snmp_agent.txt added
-
-  2006-03-29 Christiaan Simons
-  * inet.c, inet.h: Added platform byteswap support.
-    Added LWIP_PLATFORM_BYTESWAP define (defaults to 0) and
-    optional LWIP_PLATFORM_HTONS(), LWIP_PLATFORM_HTONL() macros.
-
-  ++ Bug fixes:
-
-  2006-11-30 Christiaan Simons
-  * dhcp.c: Fixed false triggers of request_timeout.
-
-  2006-11-28 Christiaan Simons
-  * netif.c: In netif_add() fixed missing clear of ip_addr, netmask, gw and flags.
-
-  2006-10-11 Christiaan Simons
-  * api_lib.c etharp.c, ip.c, memp.c, stats.c, sys.{c,h} tcp.h:
-    Partially accepted patch #5449 for ANSI C compatibility / build fixes.
-  * ipv4/lwip/ip.h ipv6/lwip/ip.h: Corrected UDP-Lite protocol
-    identifier from 170 to 136 (bug #17574).
-
-  2006-10-10 Christiaan Simons
-  * api_msg.c: Fixed Nagle algorithm as reported by Bob Grice.
-
-  2006-08-17 Christiaan Simons
-  * udp.c: Fixed bug #17200, added check for broadcast
-    destinations for PCBs bound to a unicast address.
-
-  2006-08-07 Christiaan Simons
-  * api_msg.c: Flushing TCP output in do_close() (bug #15926).
-
-  2006-06-27 Christiaan Simons
-  * api_msg.c: Applied patch for cold case (bug #11135).
-    In accept_function() ensure newconn->callback is always initialized.
-
-  2006-06-15 Christiaan Simons
-  * mem.h: added MEM_SIZE_F alias to fix an ancient cold case (bug #1748),
-    facilitate printing of mem_size_t and u16_t statistics.
-
-  2006-06-14 Christiaan Simons
-  * api_msg.c: Applied patch #5146 to handle allocation failures
-    in accept() by Kevin Lawson.
-
-  2006-05-26 Christiaan Simons
-  * api_lib.c: Removed conn->sem creation and destruction 
-    from netconn_write() and added sys_sem_new to netconn_new_*.
-
-(STABLE-1_1_1)
-
-  2006-03-03  Christiaan Simons
-  * ipv4/ip_frag.c: Added bound-checking assertions on ip_reassbitmap
-    access and added pbuf_alloc() return value checks.
-
-  2006-01-01  Leon Woestenberg <leon.woestenberg@gmx.net>
-  * tcp_{in,out}.c, tcp_out.c: Removed 'even sndbuf' fix in TCP, which is
-    now handled by the checksum routine properly.
-
-  2006-02-27  Leon Woestenberg <leon.woestenberg@gmx.net>
-   * pbuf.c: Fix alignment; pbuf_init() would not work unless
-     pbuf_pool_memory[] was properly aligned. (Patch by Curt McDowell.)
-
-  2005-12-20  Leon Woestenberg <leon.woestenberg@gmx.net>
-  * tcp.c: Remove PCBs which stay in LAST_ACK state too long. Patch
-    submitted by Mitrani Hiroshi.
-
-  2005-12-15  Christiaan Simons
-  * inet.c: Disabled the added summing routine to preserve code space.
-
-  2005-12-14  Leon Woestenberg <leon.woestenberg@gmx.net>
-  * tcp_in.c: Duplicate FIN ACK race condition fix by Kelvin Lawson.
-    Added Curt McDowell's optimized checksumming routine for future
-    inclusion. Need to create test case for unaliged, aligned, odd,
-    even length combination of cases on various endianess machines.
-
-  2005-12-09  Christiaan Simons
-  * inet.c: Rewrote standard checksum routine in proper portable C.
-
-  2005-11-25  Christiaan Simons
-  * udp.c tcp.c: Removed SO_REUSE hack. Should reside in socket code only.
-  * *.c: introduced cc.h LWIP_DEBUG formatters matching the u16_t, s16_t,
-    u32_t, s32_t typedefs. This solves most debug word-length assumes.
-
-  2005-07-17 Leon Woestenberg <leon.woestenberg@gmx.net>
-  * inet.c: Fixed unaligned 16-bit access in the standard checksum
-    routine by Peter Jolasson.
-  * slipif.c: Fixed implementation assumption of single-pbuf datagrams.
-
-  2005-02-04 Leon Woestenberg <leon.woestenberg@gmx.net>
-  * tcp_out.c: Fixed uninitialized 'queue' referenced in memerr branch.
-  * tcp_{out|in}.c: Applied patch fixing unaligned access.
-
-  2005-01-04 Leon Woestenberg <leon.woestenberg@gmx.net>
-  * pbuf.c: Fixed missing semicolon after LWIP_DEBUG statement.
-
-  2005-01-03 Leon Woestenberg <leon.woestenberg@gmx.net>
-  * udp.c: UDP pcb->recv() was called even when it was NULL.
-
-(STABLE-1_1_0)
-
-  2004-12-28 Leon Woestenberg <leon.woestenberg@gmx.net>
-  * etharp.*: Disabled multiple packets on the ARP queue.
-    This clashes with TCP queueing.
-
-  2004-11-28 Leon Woestenberg <leon.woestenberg@gmx.net>
-  * etharp.*: Fixed race condition from ARP request to ARP timeout.
-    Halved the ARP period, doubled the period counts.
-    ETHARP_MAX_PENDING now should be at least 2. This prevents
-    the counter from reaching 0 right away (which would allow
-    too little time for ARP responses to be received).
-
-  2004-11-25 Leon Woestenberg <leon.woestenberg@gmx.net>
-  * dhcp.c: Decline messages were not multicast but unicast.
-  * etharp.c: ETHARP_CREATE is renamed to ETHARP_TRY_HARD.
-    Do not try hard to insert arbitrary packet's source address,
-    etharp_ip_input() now calls etharp_update() without ETHARP_TRY_HARD. 
-    etharp_query() now always DOES call ETHARP_TRY_HARD so that users
-    querying an address will see it appear in the cache (DHCP could
-    suffer from this when a server invalidly gave an in-use address.)
-  * ipv4/ip_addr.h: Renamed ip_addr_maskcmp() to _netcmp() as we are
-    comparing network addresses (identifiers), not the network masks
-    themselves.
-  * ipv4/ip_addr.c: ip_addr_isbroadcast() now checks that the given
-    IP address actually belongs to the network of the given interface.
-
-  2004-11-24 Kieran Mansley <kjm25@cam.ac.uk>
-  * tcp.c: Increment pcb->snd_buf when ACK is received in SYN_SENT state.
-
-(STABLE-1_1_0-RC1)
-
-  2004-10-16 Kieran Mansley <kjm25@cam.ac.uk>
-  * tcp.c: Add code to tcp_recved() to send an ACK (window update) immediately,
-    even if one is already pending, if the rcv_wnd is above a threshold
-    (currently TCP_WND/2). This avoids waiting for a timer to expire to send a
-    delayed ACK in order to open the window if the stack is only receiving data.
-
-  2004-09-12 Kieran Mansley <kjm25@cam.ac.uk>
-  * tcp*.*: Retransmit time-out handling improvement by Sam Jansen.
-
-  2004-08-20 Tony Mountifield <tony@softins.co.uk>
-  * etharp.c: Make sure the first pbuf queued on an ARP entry
-    is properly ref counted.
-
-  2004-07-27 Tony Mountifield <tony@softins.co.uk>
-  * debug.h: Added (int) cast in LWIP_DEBUGF() to avoid compiler
-    warnings about comparison.
-  * pbuf.c: Stopped compiler complaining of empty if statement
-    when LWIP_DEBUGF() empty.  Closed an unclosed comment.
-  * tcp.c: Stopped compiler complaining of empty if statement
-    when LWIP_DEBUGF() empty.
-  * ip.h Corrected IPH_TOS() macro: returns a byte, so doesn't need htons().
-  * inet.c: Added a couple of casts to quiet the compiler.
-    No need to test isascii(c) before isdigit(c) or isxdigit(c).
-
-  2004-07-22 Tony Mountifield <tony@softins.co.uk>
-  * inet.c: Made data types consistent in inet_ntoa().
-    Added casts for return values of checksum routines, to pacify compiler.
-  * ip_frag.c, tcp_out.c, sockets.c, pbuf.c
-    Small corrections to some debugging statements, to pacify compiler.
-
-  2004-07-21 Tony Mountifield <tony@softins.co.uk>
-  * etharp.c: Removed spurious semicolon and added missing end-of-comment.
-  * ethernetif.c Updated low_level_output() to match prototype for
-    netif->linkoutput and changed low_level_input() similarly for consistency.
-  * api_msg.c: Changed recv_raw() from int to u8_t, to match prototype
-    of raw_recv() in raw.h and so avoid compiler error.
-  * sockets.c: Added trivial (int) cast to keep compiler happier.
-  * ip.c, netif.c Changed debug statements to use the tidier ip4_addrN() macros.
-
-(STABLE-1_0_0)
-
-  ++ Changes:
-
-  2004-07-05 Leon Woestenberg <leon.woestenberg@gmx.net>
-  * sockets.*: Restructured LWIP_PRIVATE_TIMEVAL. Make sure
-    your cc.h file defines this either 1 or 0. If non-defined,
-    defaults to 1.
-  * .c: Added <string.h> and <errno.h> includes where used.
-  * etharp.c: Made some array indices unsigned.
-
-  2004-06-27 Leon Woestenberg <leon.woestenberg@gmx.net>
-  * netif.*: Added netif_set_up()/down().
-  * dhcp.c: Changes to restart program flow.
-
-  2004-05-07 Leon Woestenberg <leon.woestenberg@gmx.net>
-  * etharp.c: In find_entry(), instead of a list traversal per candidate, do a
-    single-pass lookup for different candidates. Should exploit locality.
-
-  2004-04-29 Leon Woestenberg <leon.woestenberg@gmx.net>
-  * tcp*.c: Cleaned up source comment documentation for Doxygen processing.
-  * opt.h: ETHARP_ALWAYS_INSERT option removed to comply with ARP RFC.
-  * etharp.c: update_arp_entry() only adds new ARP entries when adviced to by
-    the caller. This deprecates the ETHARP_ALWAYS_INSERT overrule option.
-
-  ++ Bug fixes:
-
-  2004-04-27 Leon Woestenberg <leon.woestenberg@gmx.net>
-  * etharp.c: Applied patch of bug #8708 by Toni Mountifield with a solution
-    suggested by Timmy Brolin. Fix for 32-bit processors that cannot access
-    non-aligned 32-bit words, such as soms 32-bit TCP/IP header fields. Fix
-    is to prefix the 14-bit Ethernet headers with two padding bytes.
-
-  2004-04-23 Leon Woestenberg <leon.woestenberg@gmx.net>
-  * ip_addr.c: Fix in the ip_addr_isbroadcast() check.
-  * etharp.c: Fixed the case where the packet that initiates the ARP request
-    is not queued, and gets lost. Fixed the case where the packets destination
-    address is already known; we now always queue the packet and perform an ARP
-    request.
-
-(STABLE-0_7_0)
-
-  ++ Bug fixes:
-
-  * Fixed TCP bug for SYN_SENT to ESTABLISHED state transition.
-  * Fixed TCP bug in dequeueing of FIN from out of order segment queue.
-  * Fixed two possible NULL references in rare cases.
-
-(STABLE-0_6_6)
-
-  ++ Bug fixes:
-
-  * Fixed DHCP which did not include the IP address in DECLINE messages.
-
-  ++ Changes:
-
-  * etharp.c has been hauled over a bit.
-
-(STABLE-0_6_5)
-
-  ++ Bug fixes:
-
-  * Fixed TCP bug induced by bad window resizing with unidirectional TCP traffic.
-  * Packets sent from ARP queue had invalid source hardware address.
-
-  ++ Changes:
-
-  * Pass-by ARP requests do now update the cache.
-
-  ++ New features:
-
-  * No longer dependent on ctype.h.
-  * New socket options.
-  * Raw IP pcb support.
-
-(STABLE-0_6_4)
-
-  ++ Bug fixes:
-
-  * Some debug formatters and casts fixed.
-  * Numereous fixes in PPP.
-
-  ++ Changes:
-
-  * DEBUGF now is LWIP_DEBUGF
-  * pbuf_dechain() has been re-enabled.
-  * Mentioned the changed use of CVS branches in README.
-
-(STABLE-0_6_3)
-
-  ++ Bug fixes:
-
-  * Fixed pool pbuf memory leak in pbuf_alloc().
-    Occured if not enough PBUF_POOL pbufs for a packet pbuf chain.
-    Reported by Savin Zlobec.
-
-  * PBUF_POOL chains had their tot_len field not set for non-first
-    pbufs. Fixed in pbuf_alloc().
-
-  ++ New features:
-
-  * Added PPP stack contributed by Marc Boucher
-
-  ++ Changes:
-
-  * Now drops short packets for ICMP/UDP/TCP protocols. More robust.
-
-  * ARP queueuing now queues the latest packet instead of the first.
-    This is the RFC recommended behaviour, but can be overridden in
-    lwipopts.h.
-
-(0.6.2)
-
-  ++ Bugfixes:
-
-  * TCP has been fixed to deal with the new use of the pbuf->ref
-    counter.
-
-  * DHCP dhcp_inform() crash bug fixed.
-
-  ++ Changes:
-
-  * Removed pbuf_pool_free_cache and pbuf_pool_alloc_cache. Also removed
-    pbuf_refresh(). This has sped up pbuf pool operations considerably.
-    Implemented by David Haas.
-
-(0.6.1)
-
-  ++ New features:
-
-  * The packet buffer implementation has been enhanced to support
-    zero-copy and copy-on-demand for packet buffers which have their
-    payloads in application-managed memory.
-    Implemented by David Haas.
-
-    Use PBUF_REF to make a pbuf refer to RAM. lwIP will use zero-copy
-    if an outgoing packet can be directly sent on the link, or perform
-    a copy-on-demand when necessary.
-
-    The application can safely assume the packet is sent, and the RAM
-    is available to the application directly after calling udp_send()
-    or similar function.
-
-  ++ Bugfixes:
-
-  * ARP_QUEUEING should now correctly work for all cases, including
-    PBUF_REF.
-    Implemented by Leon Woestenberg.
-
-  ++ Changes:
-
-  * IP_ADDR_ANY is no longer a NULL pointer. Instead, it is a pointer
-    to a '0.0.0.0' IP address.
-
-  * The packet buffer implementation is changed. The pbuf->ref counter
-    meaning has changed, and several pbuf functions have been
-    adapted accordingly.
-
-  * netif drivers have to be changed to set the hardware address length field
-    that must be initialized correctly by the driver (hint: 6 for Ethernet MAC).
-    See the contrib/ports/c16x cs8900 driver as a driver example.
-
-  * netif's have a dhcp field that must be initialized to NULL by the driver.
-    See the contrib/ports/c16x cs8900 driver as a driver example.
-
-(0.5.x) This file has been unmaintained up to 0.6.1. All changes are
-  logged in CVS but have not been explained here.
-
-(0.5.3) Changes since version 0.5.2
-
-  ++ Bugfixes:
-
-  * memp_malloc(MEMP_API_MSG) could fail with multiple application
-    threads because it wasn't protected by semaphores.
-
-  ++ Other changes:
-
-  * struct ip_addr now packed.
-
-  * The name of the time variable in arp.c has been changed to ctime
-    to avoid conflicts with the time() function.
-
-(0.5.2) Changes since version 0.5.1
-
-  ++ New features:
-
-  * A new TCP function, tcp_tmr(), now handles both TCP timers.
-
-  ++ Bugfixes:
-
-  * A bug in tcp_parseopt() could cause the stack to hang because of a
-    malformed TCP option.
-
-  * The address of new connections in the accept() function in the BSD
-    socket library was not handled correctly.
-
-  * pbuf_dechain() did not update the ->tot_len field of the tail.
-
-  * Aborted TCP connections were not handled correctly in all
-    situations.
-
-  ++ Other changes:
-
-  * All protocol header structs are now packed.
-
-  * The ->len field in the tcp_seg structure now counts the actual
-    amount of data, and does not add one for SYN and FIN segments.
-
-(0.5.1) Changes since version 0.5.0
-
-  ++ New features:
-
-  * Possible to run as a user process under Linux.
-
-  * Preliminary support for cross platform packed structs.
-
-  * ARP timer now implemented.
-
-  ++ Bugfixes:
-
-  * TCP output queue length was badly initialized when opening
-    connections.
-
-  * TCP delayed ACKs were not sent correctly.
-
-  * Explicit initialization of BSS segment variables.
-
-  * read() in BSD socket library could drop data.
-
-  * Problems with memory alignment.
-
-  * Situations when all TCP buffers were used could lead to
-    starvation.
-
-  * TCP MSS option wasn't parsed correctly.
-
-  * Problems with UDP checksum calculation.
-
-  * IP multicast address tests had endianess problems.
-
-  * ARP requests had wrong destination hardware address.
-
-  ++ Other changes:
-
-  * struct eth_addr changed from u16_t[3] array to u8_t[6].
-
-  * A ->linkoutput() member was added to struct netif.
-
-  * TCP and UDP ->dest_* struct members where changed to ->remote_*.
-
-  * ntoh* macros are now null definitions for big endian CPUs.
-
-(0.5.0) Changes since version 0.4.2
-
-  ++ New features:
-
-  * Redesigned operating system emulation layer to make porting easier.
-
-  * Better control over TCP output buffers.
-
-  * Documenation added.
-
-  ++ Bugfixes:
-
-  * Locking issues in buffer management.
-
-  * Bugfixes in the sequential API.
-
-  * IP forwarding could cause memory leakage. This has been fixed.
-
-  ++ Other changes:
-
-  * Directory structure somewhat changed; the core/ tree has been
-    collapsed.
-
-(0.4.2) Changes since version 0.4.1
-
-  ++ New features:
-
-  * Experimental ARP implementation added.
-
-  * Skeleton Ethernet driver added.
-
-  * Experimental BSD socket API library added.
-
-  ++ Bugfixes:
-
-  * In very intense situations, memory leakage could occur. This has
-    been fixed.
-
-  ++ Other changes:
-
-  * Variables named "data" and "code" have been renamed in order to
-    avoid name conflicts in certain compilers.
-
-  * Variable++ have in appliciable cases been translated to ++variable
-    since some compilers generate better code in the latter case.
-
-(0.4.1) Changes since version 0.4
-
-  ++ New features:
-
-  * TCP: Connection attempts time out earlier than data
-    transmissions. Nagle algorithm implemented. Push flag set on the
-    last segment in a burst.
-
-  * UDP: experimental support for UDP-Lite extensions.
-
-  ++ Bugfixes:
-
-  * TCP: out of order segments were in some cases handled incorrectly,
-    and this has now been fixed. Delayed acknowledgements was broken
-    in 0.4, has now been fixed. Binding to an address that is in use
-    now results in an error. Reset connections sometimes hung an
-    application; this has been fixed.
-
-  * Checksum calculation sometimes failed for chained pbufs with odd
-    lengths. This has been fixed.
-
-  * API: a lot of bug fixes in the API. The UDP API has been improved
-    and tested. Error reporting and handling has been
-    improved. Logical flaws and race conditions for incoming TCP
-    connections has been found and removed.
-
-  * Memory manager: alignment issues. Reallocating memory sometimes
-    failed, this has been fixed.
-
-  * Generic library: bcopy was flawed and has been fixed.
-
-  ++ Other changes:
-
-  * API: all datatypes has been changed from generic ones such as
-    ints, to specified ones such as u16_t. Functions that return
-    errors now have the correct type (err_t).
-
-  * General: A lot of code cleaned up and debugging code removed. Many
-    portability issues have been fixed.
-
-  * The license was changed; the advertising clause was removed.
-
-  * C64 port added.
-
-  * Thanks: Huge thanks go to Dagan Galarneau, Horst Garnetzke, Petri
-    Kosunen, Mikael Caleres, and Frits Wilmink for reporting and
-    fixing bugs!
-
-(0.4) Changes since version 0.3.1
-
-  * Memory management has been radically changed; instead of
-    allocating memory from a shared heap, memory for objects that are
-    rapidly allocated and deallocated is now kept in pools. Allocation
-    and deallocation from those memory pools is very fast. The shared
-    heap is still present but is used less frequently.
-
-  * The memory, memory pool, and packet buffer subsystems now support
-    4-, 2-, or 1-byte alignment.
-
-  * "Out of memory" situations are handled in a more robust way.
-
-  * Stack usage has been reduced.
-
-  * Easier configuration of lwIP parameters such as memory usage,
-    TTLs, statistics gathering, etc. All configuration parameters are
-    now kept in a single header file "lwipopts.h".
-
-  * The directory structure has been changed slightly so that all
-    architecture specific files are kept under the src/arch
-    hierarchy.
-
-  * Error propagation has been improved, both in the protocol modules
-    and in the API.
-
-  * The code for the RTXC architecture has been implemented, tested
-    and put to use.
-
-  * Bugs have been found and corrected in the TCP, UDP, IP, API, and
-    the Internet checksum modules.
-
-  * Bugs related to porting between a 32-bit and a 16-bit architecture
-    have been found and corrected.
-
-  * The license has been changed slightly to conform more with the
-    original BSD license, including the advertisement clause.
-
-(0.3.1) Changes since version 0.3
-
-  * Fix of a fatal bug in the buffer management. Pbufs with allocated
-    RAM never returned the RAM when the pbuf was deallocated.
-
-  * TCP congestion control, window updates and retransmissions did not
-    work correctly. This has now been fixed.
-
-  * Bugfixes in the API.
-
-(0.3) Changes since version 0.2
-
-  * New and improved directory structure. All include files are now
-    kept in a dedicated include/ directory.
-
-  * The API now has proper error handling. A new function,
-    netconn_err(), now returns an error code for the connection in
-    case of errors.
-
-  * Improvements in the memory management subsystem. The system now
-    keeps a pointer to the lowest free memory block. A new function,
-    mem_malloc2() tries to allocate memory once, and if it fails tries
-    to free some memory and retry the allocation.
-
-  * Much testing has been done with limited memory
-    configurations. lwIP now does a better job when overloaded.
-
-  * Some bugfixes and improvements to the buffer (pbuf) subsystem.
-
-  * Many bugfixes in the TCP code:
-
-    - Fixed a bug in tcp_close().
-
-    - The TCP receive window was incorrectly closed when out of
-      sequence segments was received. This has been fixed.
-
-    - Connections are now timed-out of the FIN-WAIT-2 state.
-
-    - The initial congestion window could in some cases be too
-      large. This has been fixed.
-
-    - The retransmission queue could in some cases be screwed up. This
-      has been fixed.
-
-    - TCP RST flag now handled correctly.
-
-    - Out of sequence data was in some cases never delivered to the
-      application. This has been fixed.
-
-    - Retransmitted segments now contain the correct acknowledgment
-      number and advertised window.
-
-    - TCP retransmission timeout backoffs are not correctly computed
-      (ala BSD). After a number of retransmissions, TCP now gives up
-      the connection.
-
-  * TCP connections now are kept on three lists, one for active
-    connections, one for listening connections, and one for
-    connections that are in TIME-WAIT. This greatly speeds up the fast
-    timeout processing for sending delayed ACKs.
-
-  * TCP now provides proper feedback to the application when a
-    connection has been successfully set up.
-
-  * More comments have been added to the code. The code has also been
-    somewhat cleaned up.
-
-(0.2) Initial public release.
+HISTORY
+
+(CVS HEAD)
+
+  * [Enter new changes just after this line - do not remove this line]
+
+ ++ New features:
+
+
+ ++ Bugfixes:
+
+
+
+
+(STABLE-1.4.1)
+
+  ++ New features:
+
+  2012-03-25: Simon Goldschmidt (idea by Mason)
+  * posix/*: added posix-compatibility include files posix/netdb.h and posix/sys/socket.h
+    which are a simple wrapper to the correct lwIP include files.
+ 
+  2012-01-16: Simon Goldschmidt
+  * opt.h, icmp.c: Added option CHECKSUM_GEN_ICMP
+
+  2011-12-17: Simon Goldschmidt
+  * ip.h: implemented API functions to access so_options of IP pcbs (UDP, TCP, RAW)
+    (fixes bug #35061)
+
+  2011-09-27: Simon Goldschmidt
+  * opt.h, tcp.c, tcp_in.c: Implemented limiting data on ooseq queue (task #9989)
+    (define TCP_OOSEQ_MAX_BYTES / TCP_OOSEQ_MAX_PBUFS in lwipopts.h)
+
+  2011-09-21: Simon Goldschmidt
+  * opt.h, api.h, api_lib.c, api_msg.h/.c, sockets.c: Implemented timeout on
+    send (TCP only, bug #33820)
+
+  2011-09-21: Simon Goldschmidt
+  * init.c: Converted runtime-sanity-checks into compile-time checks that can
+    be disabled (since runtime checks can often not be seen on embedded targets)
+
+  2011-09-11: Simon Goldschmidt
+  * ppp.h, ppp_impl.h: splitted ppp.h to an internal and external header file
+    to get a clear separation of which functions an application or port may use
+    (task #11281)
+
+ 2011-09-11: Simon Goldschmidt
+  * opt.h, tcp_impl.h, tcp.c, udp.h/.c: Added a config option to randomize
+    initial local TCP/UDP ports (so that different port ranges are used after
+    a reboot; bug #33818; this one added tcp_init/udp_init functions again)
+
+  2011-09-03: Simon Goldschmidt
+  * dhcp.c: DHCP uses LWIP_RAND() for xid's (bug #30302)
+
+  2011-08-24: Simon Goldschmidt
+  * opt.h, netif.h/.c: added netif remove callback (bug #32397)
+
+  2011-07-26: Simon Goldschmidt
+  * etharp.c: ETHARP_SUPPORT_VLAN: add support for an external VLAN filter
+    function instead of only checking for one VLAN (define ETHARP_VLAN_CHECK_FN)
+
+  2011-07-21: Simon Goldschmidt (patch by hanhui)
+  * ip4.c, etharp.c, pbuf.h: bug #33634 ip_forward() have a faulty behaviour:
+    Added pbuf flags to mark incoming packets as link-layer broadcast/multicast.
+    Also added code to allow ip_forward() to forward non-broadcast packets to
+    the input netif (set IP_FORWARD_ALLOW_TX_ON_RX_NETIF==1).
+
+  2011-06-26: Simon Goldschmidt (patch by Cameron Gutman)
+  * tcp.c, tcp_out.c: bug #33604: added some more asserts to check that
+    pcb->state != LISTEN
+
+   2011-05-14: Simon Goldschmidt (patch by Stéphane Lesage)
+  * tcpip.c/.h: patch #7449 allow tcpip callback from interrupt with static
+    memory message
+
+
+ ++ Bugfixes:
+
+  2012-09-26: Simon Goldschmidt
+  * api_msg.c: fixed bug #37405 'err_tcp()' uses already freed 'netconn' object
+
+  2012-09-26: patch by Henrik Persson
+  * dhcp.c: patch #7843 Fix corner case with dhcp timeouts
+
+  2012-09-26: patch by Henrik Persson
+  * dhcp.c: patch #7840 Segfault in dhcp_parse_reply if no end marker in dhcp packet
+
+  2012-08-22: Simon Goldschmidt
+  * memp.c: fixed bug #37166: memp_sanity check loops itself
+
+  2012-05-08: Simon Goldschmidt
+  * tcp_out.c: fixed bug: #36380 unsent_oversize mismatch in 1.4.1RC1 (this was
+    a debug-check issue only)
+
+  2012-03-27: Simon Goldschmidt
+  * vj.c: fixed bug #35756 header length calculation problem in ppp/vj.c
+
+  2012-03-27: Simon Goldschmidt (patch by Mason)
+  * tcp_out.c: fixed bug #35945: SYN packet should provide the recv MSS not the
+    send MSS
+
+  2012-03-22: Simon Goldschmidt
+  * ip4.c: fixed bug #35927: missing refragmentaion in ip_forward
+ 
+  2012-03-20: Simon Goldschmidt (patch by Mason)
+  * netdb.c: fixed bug #35907: lwip_gethostbyname_r returns an invalid h_addr_list
+ 
+  2012-03-12: Simon Goldschmidt (patch by Bostjan Meglic)
+  * ppp.c: fixed bug #35809: PPP GetMask(): Compiler warning on big endian,
+    possible bug on little endian system
+
+  2012-02-23: Simon Goldschmidt
+  * etharp.c: fixed bug #35595: Impossible to send broadcast without a gateway
+    (introduced when fixing bug# 33551)
+
+  2012-02-16: Simon Goldschmidt
+  * ppp.c: fixed pbuf leak when PPP session is aborted through pppSigHUP()
+    (bug #35541: PPP Memory Leak)
+
+  2012-02-16: Simon Goldschmidt
+  * etharp.c: fixed bug #35531: Impossible to send multicast without a gateway
+    (introduced when fixing bug# 33551)
+
+  2012-02-16: Simon Goldschmidt (patch by Stéphane Lesage)
+  * msg_in.c, msg_out.c: fixed bug #35536 SNMP: error too big response is malformed
+
+  2012-02-15: Simon Goldschmidt
+  * init.c: fixed bug #35537: MEMP_NUM_* sanity checks should be disabled with
+    MEMP_MEM_MALLOC==1
+
+  2012-02-12: Simon Goldschmidt
+  * tcp.h, tcp_in.c, tcp_out.c: partly fixed bug #25882: TCP hangs on
+    MSS > pcb->snd_wnd (by not creating segments bigger than half the window)
+
+  2012-02-11: Simon Goldschmidt
+  * tcp.c: fixed bug #35435: No pcb state check before adding it to time-wait
+    queue while closing
+
+  2012-01-22: Simon Goldschmidt
+  * tcp.c, tcp_in.c: fixed bug #35305: pcb may be freed too early on shutdown(WR)
+
+  2012-01-21: Simon Goldschmidt
+  * tcp.c: fixed bug #34636: FIN_WAIT_2 - Incorrect shutdown of TCP pcb
+
+  2012-01-20: Simon Goldschmidt
+  * dhcp.c: fixed bug #35151: DHCP asserts on incoming option lengths
+
+ 2012-01-20: Simon Goldschmidt
+  * pbuf.c: fixed bug #35291: NULL pointer in pbuf_copy
+
+  2011-11-25: Simon Goldschmidt
+  * tcp.h/.c, tcp_impl.h, tcp_in.c: fixed bug #31177: tcp timers can corrupt
+    tcp_active_pcbs in some cases
+
+  2011-11-23: Simon Goldschmidt
+  * sys.c: fixed bug #34884: sys_msleep() body needs to be surrounded with
+    '#ifndef sys_msleep'
+
+  2011-11-22: Simon Goldschmidt
+  * netif.c, etharp.h/.c: fixed bug #34684: Clear the arp table cache when
+    netif is brought down
+
+  2011-10-28: Simon Goldschmidt
+  * tcp_in.c: fixed bug #34638: Dead code in tcp_receive - pcb->dupacks
+
+  2011-10-23: Simon Goldschmidt
+  * mem.c: fixed bug #34429: possible memory corruption with
+    LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT set to 1
+
+  2011-10-18: Simon Goldschmidt
+  * arch.h, netdb.c: fixed bug #34592: lwip_gethostbyname_r uses nonstandard
+    error value
+
+  2011-10-18: Simon Goldschmidt
+  * opt.h: fixed default values of TCP_SNDLOWAT and TCP_SNDQUEUELOWAT for small
+    windows (bug #34176 select after non-blocking send times out)
+
+  2011-10-18: Simon Goldschmidt
+  * tcp_impl.h, tcp_out.c: fixed bug #34587: TCP_BUILD_MSS_OPTION doesn't
+    consider netif->mtu, causes slow network
+
+  2011-10-18: Simon Goldschmidt
+  * sockets.c: fixed bug #34581 missing parentheses in udplite sockets code
+
+  2011-10-18: Simon Goldschmidt
+  * sockets.h: fixed bug #34580 fcntl() is missing in LWIP_COMPAT_SOCKETS
+
+  2011-10-17: Simon Goldschmidt
+  * api_msg.c: fixed bug #34569: shutdown(SHUT_WR) crashes netconn/socket api
+
+  2011-10-13: Simon Goldschmidt
+  * tcp_in.c, tcp_out.c: fixed bug #34517 (persist timer is started although no
+    zero window is received) by starting the persist timer when a zero window is
+    received, not when we have more data queued for sending than fits into the
+    window
+
+  2011-10-13: Simon Goldschmidt
+  * def.h, timers.c: fixed bug #34541: LWIP_U32_DIFF is unnecessarily complex
+
+  2011-10-13: Simon Goldschmidt
+  * sockets.c, api_lib.c: fixed bug #34540: compiler error when CORE_LOCKING is
+    used and not all protocols are enabled
+
+  2011-10-12: Simon Goldschmidt
+  * pbuf.c: fixed bug #34534: Error in sending fragmented IP if MEM_ALIGNMENT > 4
+
+  2011-10-09: Simon Goldschmidt
+  * tcp_out.c: fixed bug #34426: tcp_zero_window_probe() transmits incorrect
+    byte value when pcb->unacked != NULL
+
+  2011-10-09: Simon Goldschmidt
+  * ip4.c: fixed bug #34447 LWIP_IP_ACCEPT_UDP_PORT(dst_port) wrong
+
+  2011-09-27: Simon Goldschmidt
+  * tcp_in.c, tcp_out.c: Reset pcb->unsent_oversize in 2 more places...
+
+  2011-09-27: Simon Goldschmidt
+  * tcp_in.c: fixed bug #28288: Data after FIN in oos queue
+
+  2011-09-27: Simon Goldschmidt
+  * dhcp.c: fixed bug #34406 dhcp_option_hostname() can overflow the pbuf
+
+  2011-09-24: Simon Goldschmidt
+  * mem.h: fixed bug #34377 MEM_SIZE_F is not defined if MEM_LIBC_MALLOC==1
+
+  2011-09-23: Simon Goldschmidt
+  * pbuf.h, tcp.c, tcp_in.c: fixed bug #33871: rejecting TCP_EVENT_RECV() for
+    the last packet including FIN can lose data
+
+  2011-09-22: Simon Goldschmidt
+  * tcp_impl.h: fixed bug #34355: nagle does not take snd_buf/snd_queuelen into
+    account
+
+  2011-09-21: Simon Goldschmidt
+  * opt.h: fixed default value of TCP_SND_BUF to not violate the sanity checks
+    in init.c
+
+  2011-09-20: Simon Goldschmidt
+  * timers.c: fixed bug #34337 (possible NULL pointer in sys_check_timeouts)
+
+  2011-09-11: Simon Goldschmidt
+  * tcp_out.c: use pcb->mss instead of TCP_MSS for preallocate mss-sized pbufs
+    (bug #34019)
+
+  2011-09-09: Simon Goldschmidt
+  * udp.c: fixed bug #34072: UDP broadcast is received from wrong UDP pcb if
+    udp port matches
+
+  2011-09-03: Simon Goldschmidt
+  * tcp_in.c: fixed bug #33952 PUSH flag in incoming packet is lost when packet
+    is aggregated and sent to application
+
+  2011-09-01: Simon Goldschmidt
+  * opt.h: fixed bug #31809 LWIP_EVENT_API in opts.h is inconsistent compared
+    to other options
+
+  2011-09-01: Simon Goldschmidt
+  * tcp_in.c: fixed bug #34111 RST for ACK to listening pcb has wrong seqno
+
+  2011-08-24: Simon Goldschmidt
+  * api_msg.c, sockets.c: fixed bug #33956 Wrong error returned when calling
+    accept() on UDP connections
+
+  2011-08-24: Simon Goldschmidt
+  * sockets.h: fixed bug #34057 socklen_t should be a typedef
+
+  2011-08-24: Simon Goldschmidt
+  * pbuf.c: fixed bug #34112 Odd check in pbuf_alloced_custom (typo)
+
+  2011-08-24: Simon Goldschmidt
+  * dhcp.c: fixed bug #34122 dhcp: hostname can overflow
+
+  2011-08-24: Simon Goldschmidt
+  * netif.c: fixed bug #34121 netif_add/netif_set_ipaddr fail on NULL ipaddr
+
+  2011-08-22: Simon Goldschmidt
+  * tcp_out.c: fixed bug #33962 TF_FIN not always set after FIN is sent. (This
+    merely prevents nagle from not transmitting fast after closing.)
+
+  2011-07-22: Simon Goldschmidt
+  * api_lib.c, api_msg.c, sockets.c, api.h: fixed bug #31084 (socket API returns
+    always EMSGSIZE on non-blocking sockets if data size > send buffers) -> now
+    lwip_send() sends as much as possible for non-blocking sockets
+
+  2011-07-22: Simon Goldschmidt
+  * pbuf.c/.h, timers.c: freeing ooseq pbufs when the pbuf pool is empty implemented
+    for NO_SYS==1: when not using sys_check_timeouts(), call PBUF_CHECK_FREE_OOSEQ()
+    at regular intervals from main level.
+
+  2011-07-21: Simon Goldschmidt
+  * etharp.c: fixed bug #33551 (ARP entries may time out although in use) by
+    sending an ARP request when an ARP entry is used in the last minute before
+    it would time out.
+
+  2011-07-04: Simon Goldschmidt
+  * sys_arch.txt: Fixed documentation after changing sys arch prototypes for 1.4.0.
+
+  2011-06-26: Simon Goldschmidt
+  * tcp.c: fixed bug #31723 (tcp_kill_prio() kills pcbs with the same prio) by
+    updating its documentation only.
+
+ 2011-06-26: Simon Goldschmidt
+  * mem.c: fixed bug #33545: With MEM_USE_POOLS==1, mem_malloc can return an
+    unaligned pointer.
+
+  2011-06-26: Simon Goldschmidt
+  * mem.c: fixed bug #33544 "warning in mem.c in lwip 1.4.0 with NO_SYS=1"
+
+   2011-05-25: Simon Goldschmidt
+  * tcp.c: fixed bug #33398 (pointless conversion when checking TCP port range)
+
+
+
+(STABLE-1.4.0)
+
+  ++ New features:
+
+  2011-03-27: Simon Goldschmidt
+  * tcp_impl.h, tcp_in.c, tcp_out.c: Removed 'dataptr' from 'struct tcp_seg' and
+    calculate it in tcp_zero_window_probe (the only place where it was used).
+
+  2010-11-21: Simon Goldschmidt
+  * dhcp.c/.h: Added a function to deallocate the struct dhcp from a netif
+    (fixes bug #31525).
+
+  2010-07-12: Simon Goldschmidt (patch by Stephane Lesage)
+  * ip.c, udp.c/.h, pbuf.h, sockets.c: task #10495: Added support for
+    IP_MULTICAST_LOOP at socket- and raw-API level.
+
+  2010-06-16: Simon Goldschmidt
+  * ip.c: Added an optional define (LWIP_IP_ACCEPT_UDP_PORT) that can allow
+    link-layer-addressed UDP traffic to be received while a netif is down (just
+    like DHCP during configuration)
+
+  2010-05-22: Simon Goldschmidt
+  * many many files: bug #27352: removed packing from ip_addr_t, the packed
+    version is now only used in protocol headers. Added global storage for
+    current src/dest IP address while in input functions.
+
+  2010-05-16: Simon Goldschmidt
+  * def.h: task #10391: Add preprocessor-macros for compile-time htonl
+    calculation (and use them throughout the stack where applicable)
+
+  2010-05-16: Simon Goldschmidt
+  * opt.h, memp_std.h, memp.c, ppp_oe.h/.c: PPPoE now uses its own MEMP pool
+    instead of the heap (moved struct pppoe_softc from ppp_oe.c to ppp_oe.h)
+
+  2010-05-16: Simon Goldschmidt
+  * opt.h, memp_std.h, dns.h/.c: DNS_LOCAL_HOSTLIST_IS_DYNAMIC uses its own
+    MEMP pool instead of the heap
+
+  2010-05-13: Simon Goldschmidt
+  * tcp.c, udp.c: task #6995: Implement SO_REUSEADDR (correctly), added
+    new option SO_REUSE_RXTOALL to pass received UDP broadcast/multicast
+    packets to more than one pcb.
+
+  2010-05-02: Simon Goldschmidt
+  * netbuf.h/.c, sockets.c, api_msg.c: use checksum-on-copy for sending
+    UDP data for LWIP_NETIF_TX_SINGLE_PBUF==1
+
+  2010-04-30: Simon Goldschmidt
+  * udp.h/.c, pbuf.h/.c: task #6849: added udp_send(_to/_if) functions that
+    take a precalculated checksum, added pbuf_fill_chksum() to copy data
+    into a pbuf and at the same time calculating the checksum for that data
+
+  2010-04-29: Simon Goldschmidt
+  * ip_addr.h, etharp.h/.c, autoip.c: Create overridable macros for copying
+    2-byte-aligned IP addresses and MAC addresses
+
+  2010-04-28: Patch by Bill Auerbach
+  * ip.c: Inline generating IP checksum to save a function call
+
+  2010-04-14: Simon Goldschmidt
+  * tcpip.h/.c, timers.c: Added an overridable define to get informed when the
+    tcpip_thread processes messages or timeouts to implement a watchdog.
+
+  2010-03-28: Simon Goldschmidt
+  * ip_frag.c: create a new (contiguous) PBUF_RAM for every outgoing
+    fragment if LWIP_NETIF_TX_SINGLE_PBUF==1
+
+  2010-03-27: Simon Goldschmidt
+  * etharp.c: Speedup TX by moving code from find_entry to etharp_output/
+    etharp_query to prevent unnecessary function calls (inspired by
+    patch #7135).
+
+  2010-03-20: Simon Goldschmidt
+  * opt.h, tcpip.c/.h: Added an option to disable tcpip_(un)timeout code
+    since the linker cannot do this automatically to save space.
+
+  2010-03-20: Simon Goldschmidt
+  * opt.h, etharp.c/.h: Added support for static ARP table entries
+
+  2010-03-14: Simon Goldschmidt
+  * tcp_impl.h, tcp_out.c, inet_chksum.h/.c: task #6849: Calculate checksum
+    when creating TCP segments, not when (re-)transmitting them.
+
+  2010-03-07: Simon Goldschmidt
+  * sockets.c: bug #28775 (select/event_callback: only check select_cb_list
+    on change) plus use SYS_LIGHTWEIGHT_PROT to protect the select code.
+    This should speed up receiving data on sockets as the select code in
+    event_callback is only executed when select is waiting.
+
+  2010-03-06: Simon Goldschmidt
+  * tcp_out.c: task #7013 (Create option to have all packets delivered to
+    netif->output in one piece): Always copy to try to create single pbufs
+    in tcp_write.
+
+  2010-03-06: Simon Goldschmidt
+  * api.h, api_lib.c, sockets.c: task #10167 (sockets: speed up TCP recv
+    by not allocating a netbuf): added function netconn_recv_tcp_pbuf()
+    for tcp netconns to receive pbufs, not netbufs; use that function
+    for tcp sockets.
+
+  2010-03-05: Jakob Ole Stoklundsen / Simon Goldschmidt
+  * opt.h, tcp.h, tcp_impl.h, tcp.c, tcp_in.c, tcp_out.c: task #7040:
+    Work on tcp_enqueue: Don't waste memory when chaining segments,
+    added option TCP_OVERSIZE to prevent creating many small pbufs when
+    calling tcp_write with many small blocks of data. Instead, pbufs are
+    allocated larger than needed and the space is used for later calls to
+    tcp_write.
+
+  2010-02-21: Simon Goldschmidt
+  * stats.c/.h: Added const char* name to mem- and memp-stats for easier
+    debugging.
+
+  2010-02-21: Simon Goldschmidt
+  * tcp.h (and usages), added tcp_impl.h: Splitted API and internal
+    implementation of tcp to make API usage cleare to application programmers
+
+  2010-02-14: Simon Goldschmidt/Stephane Lesage
+  * ip_addr.h: Improved some defines working on ip addresses, added faster
+    macro to copy addresses that cannot be NULL
+
+  2010-02-13: Simon Goldschmidt
+  * api.h, api_lib.c, api_msg.c, sockets.c: task #7865 (implement non-
+    blocking send operation)
+
+  2010-02-12: Simon Goldschmidt
+  * sockets.c/.h: Added a minimal version of posix fctl() to have a
+    standardised way to set O_NONBLOCK for nonblocking sockets.
+
+  2010-02-12: Simon Goldschmidt
+  * dhcp.c/.h, autoip.c/.h: task #10139 (Prefer statically allocated
+    memory): added autoip_set_struct() and dhcp_set_struct() to let autoip
+    and dhcp work with user-allocated structs instead of callin mem_malloc
+
+  2010-02-12: Simon Goldschmidt/Jeff Barber
+  * tcp.c/h: patch #6865 (SO_REUSEADDR for TCP): if pcb.so_options has
+    SOF_REUSEADDR set, allow binding to endpoint in TIME_WAIT
+
+  2010-02-12: Simon Goldschmidt
+  * sys layer: task #10139 (Prefer statically allocated memory): converted
+    mbox and semaphore functions to take pointers to sys_mbox_t/sys_sem_t;
+    converted sys_mbox_new/sys_sem_new to take pointers and return err_t;
+    task #7212: Add Mutex concept in sys_arch (define LWIP_COMPAT_MUTEX
+    to let sys.h use binary semaphores instead of mutexes - as before)
+
+  2010-02-09: Simon Goldschmidt (Simon Kallweit)
+  * timers.c/.h: Added function sys_restart_timeouts() from patch #7085
+    (Restart system timeout handling)
+
+  2010-02-09: Simon Goldschmidt
+  * netif.c/.h, removed loopif.c/.h: task #10153 (Integrate loopif into
+    netif.c) - loopif does not have to be created by the port any more,
+    just define LWIP_HAVE_LOOPIF to 1.
+
+  2010-02-08: Simon Goldschmidt
+  * inet.h, ip_addr.c/.h: Added reentrant versions of inet_ntoa/ipaddr_ntoa
+    inet_ntoa_r/ipaddr_ntoa_r
+
+  2010-02-08: Simon Goldschmidt
+  * netif.h: Added netif_s/get_igmp_mac_filter() macros
+
+  2010-02-05: Simon Goldschmidt
+  * netif.h: Added function-like macros to get/set the hostname on a netif
+
+  2010-02-04: Simon Goldschmidt
+  * nearly every file: Replaced struct ip_addr by typedef ip_addr_t to
+    make changing the actual implementation behind the typedef easier.
+
+  2010-02-01: Simon Goldschmidt
+  * opt.h, memp_std.h, dns.h, netdb.c, memp.c: Let netdb use a memp pool
+    for allocating memory when getaddrinfo() is called.
+
+  2010-01-31: Simon Goldschmidt
+  * dhcp.h, dhcp.c: Reworked the code that parses DHCP options: parse
+    them once instead of parsing for every option. This also removes
+    the need for mem_malloc from dhcp_recv and makes it possible to
+    correctly retrieve the BOOTP file.
+
+  2010-01-30: simon Goldschmidt
+  * sockets.c: Use SYS_LIGHTWEIGHT_PROT instead of a semaphore to protect
+    the sockets array.
+
+  2010-01-29: Simon Goldschmidt (patch by Laura Garrett)
+  * api.h, api_msg.c, sockets.c: Added except set support in select
+    (patch #6860)
+
+  2010-01-29: Simon Goldschmidt (patch by Laura Garrett)
+  * api.h, sockets.h, err.h, api_lib.c, api_msg.c, sockets.c, err.c:
+    Add non-blocking support for connect (partly from patch #6860),
+    plus many cleanups in socket & netconn API.
+
+  2010-01-27: Simon Goldschmidt
+  * opt.h, tcp.h, init.c, api_msg.c: Added TCP_SNDQUEUELOWAT corresponding
+    to TCP_SNDLOWAT and added tcp_sndqueuelen() - this fixes bug #28605
+
+  2010-01-26: Simon Goldschmidt
+  * snmp: Use memp pools for snmp instead of the heap; added 4 new pools.
+
+  2010-01-14: Simon Goldschmidt
+  * ppp.c/.h: Fixed bug #27856: PPP: Set netif link- and status-callback
+    by adding ppp_set_netif_statuscallback()/ppp_set_netif_linkcallback()
+
+  2010-01-13: Simon Goldschmidt
+  * mem.c: The heap now may be moved to user-defined memory by defining
+    LWIP_RAM_HEAP_POINTER as a void pointer to that memory's address
+    (patch #6966 and bug #26133)
+
+  2010-01-10: Simon Goldschmidt (Bill Auerbach)
+  * opt.h, memp.c: patch #6822 (Add option to place memory pools in
+    separate arrays)
+
+  2010-01-10: Simon Goldschmidt
+  * init.c, igmp.c: patch #6463 (IGMP - Adding Random Delay): added define
+    LWIP_RAND() for lwip-wide randomization (to be defined in cc.h)
+
+  2009-12-31: Simon Goldschmidt
+  * tcpip.c, init.c, memp.c, sys.c, memp_std.h, sys.h, tcpip.h
+    added timers.c/.h: Separated timer implementation from semaphore/mbox
+    implementation, moved timer implementation to timers.c/.h, timers are
+    now only called from tcpip_thread or by explicitly checking them.
+    (TASK#7235)
+
+  2009-12-27: Simon Goldschmidt
+  * opt.h, etharp.h/.c, init.c, tcpip.c: Added an additional option
+    LWIP_ETHERNET to support ethernet without ARP (necessary for pure PPPoE)
+
+
+  ++ Bugfixes:
+
+  2011-04-20: Simon Goldschmidt
+  * sys_arch.txt: sys_arch_timeouts() is not needed any more.
+
+  2011-04-13: Simon Goldschmidt
+  * tcp.c, udp.c: Fixed bug #33048 (Bad range for IP source port numbers) by
+    using ports in the IANA private/dynamic range (49152 through 65535).
+
+  2011-03-29: Simon Goldschmidt, patch by Emil Lhungdahl:
+  * etharp.h/.c: Fixed broken VLAN support.
+
+  2011-03-27: Simon Goldschmidt
+  * tcp.c: Fixed bug #32926 (TCP_RMV(&tcp_bound_pcbs) is called on unbound tcp
+    pcbs) by checking if the pcb was bound (local_port != 0).
+
+  2011-03-27: Simon Goldschmidt
+  * ppp.c: Fixed bug #32280 (ppp: a pbuf is freed twice)
+
+  2011-03-27: Simon Goldschmidt
+  * sockets.c: Fixed bug #32906: lwip_connect+lwip_send did not work for udp and
+    raw pcbs with LWIP_TCPIP_CORE_LOCKING==1.
+  
+  2011-03-27: Simon Goldschmidt
+  * tcp_out.c: Fixed bug #32820 (Outgoing TCP connections created before route
+    is present never times out) by starting retransmission timer before checking
+    route.
+
+  2011-03-22: Simon Goldschmidt
+  * ppp.c: Fixed bug #32648 (PPP code crashes when terminating a link) by only
+    calling sio_read_abort() if the file descriptor is valid.
+
+  2011-03-14: Simon Goldschmidt
+  * err.h/.c, sockets.c, api_msg.c: fixed bug #31748 (Calling non-blocking connect
+    more than once can render a socket useless) since it mainly involves changing
+    "FATAL" classification of error codes: ERR_USE and ERR_ISCONN just aren't fatal.
+
+  2011-03-13: Simon Goldschmidt
+  * sockets.c: fixed bug #32769 (ESHUTDOWN is linux-specific) by fixing
+    err_to_errno_table (ERR_CLSD: ENOTCONN instead of ESHUTDOWN), ERR_ISCONN:
+    use EALRADY instead of -1
+
+  2011-03-13: Simon Goldschmidt
+  * api_lib.c: netconn_accept: return ERR_ABRT instead of ERR_CLSD if the
+    connection has been aborted by err_tcp (since this is not a normal closing
+    procedure).
+
+  2011-03-13: Simon Goldschmidt
+  * tcp.c: tcp_bind: return ERR_VAL instead of ERR_ISCONN when trying to bind
+    with pcb->state != CLOSED
+
+  2011-02-17: Simon Goldschmidt
+  * rawapi.txt: Fixed bug #32561 tcp_poll argument definition out-of-order in
+    documentation
+
+  2011-02-17: Simon Goldschmidt
+  * many files: Added missing U/UL modifiers to fix 16-bit-arch portability.
+
+  2011-01-24: Simon Goldschmidt
+  * sockets.c: Fixed bug #31741: lwip_select seems to have threading problems
+
+  2010-12-02: Simon Goldschmidt
+  * err.h: Fixed ERR_IS_FATAL so that ERR_WOULDBLOCK is not fatal.
+
+  2010-11-23: Simon Goldschmidt
+  * api.h, api_lib.c, api_msg.c, sockets.c: netconn.recv_avail is only used for
+    LWIP_SO_RCVBUF and ioctl/FIONREAD.
+
+  2010-11-23: Simon Goldschmidt
+  * etharp.c: Fixed bug #31720: ARP-queueing: RFC 1122 recommends to queue at
+    least 1 packet -> ARP_QUEUEING==0 now queues the most recent packet.
+
+  2010-11-23: Simon Goldschmidt
+  * tcp_in.c: Fixed bug #30577: tcp_input: don't discard ACK-only packets after
+    refusing 'refused_data' again.
+  
+  2010-11-22: Simon Goldschmidt
+  * sockets.c: Fixed bug #31590: getsockopt(... SO_ERROR ...) gives EINPROGRESS
+    after a successful nonblocking connection.
+
+  2010-11-22: Simon Goldschmidt
+  * etharp.c: Fixed bug #31722: IP packets sent with an AutoIP source addr
+    must be sent link-local
+
+  2010-11-22: Simon Goldschmidt
+  * timers.c: patch #7329: tcp_timer_needed prototype was ifdef'ed out for
+    LWIP_TIMERS==0
+
+  2010-11-20: Simon Goldschmidt
+  * sockets.c: Fixed bug #31170: lwip_setsockopt() does not set socket number
+
+  2010-11-20: Simon Goldschmidt
+  * sockets.h: Fixed bug #31304: Changed SHUT_RD, SHUT_WR and SHUT_RDWR to
+    resemble other stacks.
+
+  2010-11-20: Simon Goldschmidt
+  * dns.c: Fixed bug #31535: TCP_SND_QUEUELEN must be at least 2 or else
+    no-copy TCP writes will never succeed.
+
+  2010-11-20: Simon Goldschmidt
+  * dns.c: Fixed bug #31701: Error return value from dns_gethostbyname() does
+    not match documentation: return ERR_ARG instead of ERR_VAL if not
+    initialized or wrong argument.
+
+  2010-10-20: Simon Goldschmidt
+  * sockets.h: Fixed bug #31385: sizeof(struct sockaddr) is 30 but should be 16
+
+  2010-10-05: Simon Goldschmidt
+  * dhcp.c: Once again fixed #30038: DHCP/AutoIP cooperation failed when
+    replugging the network cable after an AutoIP address was assigned.
+
+  2010-08-10: Simon Goldschmidt
+  * tcp.c: Fixed bug #30728: tcp_new_port() did not check listen pcbs
+
+  2010-08-03: Simon Goldschmidt
+  * udp.c, raw.c: Don't chain empty pbufs when sending them (fixes bug #30625)
+
+  2010-08-01: Simon Goldschmidt (patch by Greg Renda)
+  * ppp.c: Applied patch #7264 (PPP protocols are rejected incorrectly on big
+    endian architectures)
+  
+  2010-07-28: Simon Goldschmidt
+  * api_lib.c, api_msg.c, sockets.c, mib2.c: Fixed compilation with TCP or UDP
+    disabled.
+  
+  2010-07-27: Simon Goldschmidt
+  * tcp.c: Fixed bug #30565 (tcp_connect() check bound list): that check did no
+    harm but never did anything
+  
+  2010-07-21: Simon Goldschmidt
+  * ip.c: Fixed invalid fix for bug #30402 (CHECKSUM_GEN_IP_INLINE does not
+    add IP options)
+
+  2010-07-16: Kieran Mansley
+  * msg_in.c: Fixed SNMP ASN constant defines to not use ! operator 
+
+  2010-07-10: Simon Goldschmidt
+  * ip.c: Fixed bug #30402: CHECKSUM_GEN_IP_INLINE does not add IP options
+
+  2010-06-30: Simon Goldschmidt
+  * api_msg.c: fixed bug #30300 (shutdown parameter was not initialized in
+    netconn_delete)
+
+  2010-06-28: Kieran Mansley
+  * timers.c remove unportable printing of C function pointers
+
+  2010-06-24: Simon Goldschmidt
+  * init.c, timers.c/.h, opt.h, memp_std.h: From patch #7221: added flag
+    NO_SYS_NO_TIMERS to drop timer support for NO_SYS==1 for easier upgrading
+
+  2010-06-24: Simon Goldschmidt
+  * api(_lib).c/.h, api_msg.c/.h, sockets.c/.h: Fixed bug #10088: Correctly
+    implemented shutdown at socket level.
+
+  2010-06-21: Simon Goldschmidt
+  * pbuf.c/.h, ip_frag.c/.h, opt.h, memp_std.h: Fixed bug #29361 (ip_frag has
+    problems with zero-copy DMA MACs) by adding custom pbufs and implementing
+    custom pbufs that reference other (original) pbufs. Additionally set
+    IP_FRAG_USES_STATIC_BUF=0 as default to be on the safe side.
+
+  2010-06-15: Simon Goldschmidt
+  * dhcp.c: Fixed bug #29970: DHCP endian issue parsing option responses
+
+  2010-06-14: Simon Goldschmidt
+  * autoip.c: Fixed bug #30039: AutoIP does not reuse previous addresses
+
+  2010-06-12: Simon Goldschmidt
+  * dhcp.c: Fixed bug #30038: dhcp_network_changed doesn't reset AUTOIP coop
+    state
+
+  2010-05-17: Simon Goldschmidt
+  * netdb.c: Correctly NULL-terminate h_addr_list
+
+  2010-05-16: Simon Goldschmidt
+  * def.h/.c: changed the semantics of LWIP_PREFIX_BYTEORDER_FUNCS to prevent
+    "symbol already defined" i.e. when linking to winsock
+
+  2010-05-05: Simon Goldschmidt
+  * def.h, timers.c: Fixed bug #29769 (sys_check_timeouts: sys_now() may
+    overflow)
+
+  2010-04-21: Simon Goldschmidt
+  * api_msg.c: Fixed bug #29617 (sometime cause stall on delete listening
+    connection)
+
+  2010-03-28: Luca Ceresoli
+  * ip_addr.c/.h: patch #7143: Add a few missing const qualifiers
+
+  2010-03-27: Luca Ceresoli
+  * mib2.c: patch #7130: remove meaningless const qualifiers
+
+  2010-03-26: Simon Goldschmidt
+  * tcp_out.c: Make LWIP_NETIF_TX_SINGLE_PBUF work for TCP, too
+
+  2010-03-26: Simon Goldschmidt
+  * various files: Fixed compiling with different options disabled (TCP/UDP),
+    triggered by bug #29345; don't allocate acceptmbox if LWIP_TCP is disabled
+
+  2010-03-25: Simon Goldschmidt
+  * sockets.c: Fixed bug #29332: lwip_select() processes readset incorrectly
+
+  2010-03-25: Simon Goldschmidt
+  * tcp_in.c, test_tcp_oos.c: Fixed bug #29080: Correctly handle remote side
+    overrunning our rcv_wnd in ooseq case.
+
+  2010-03-22: Simon Goldschmidt
+  * tcp.c: tcp_listen() did not copy the pcb's prio.
+
+  2010-03-19: Simon Goldschmidt
+  * snmp_msg.c: Fixed bug #29256: SNMP Trap address was not correctly set
+
+  2010-03-14: Simon Goldschmidt
+  * opt.h, etharp.h: Fixed bug #29148 (Incorrect PBUF_POOL_BUFSIZE for ports
+    where ETH_PAD_SIZE > 0) by moving definition of ETH_PAD_SIZE to opt.h
+    and basing PBUF_LINK_HLEN on it.
+
+  2010-03-08: Simon Goldschmidt
+  * netif.c, ipv4/ip.c: task #10241 (AutoIP: don't break existing connections
+    when assiging routable address): when checking incoming packets and
+    aborting existing connection on address change, filter out link-local
+    addresses.
+
+  2010-03-06: Simon Goldschmidt
+  * sockets.c: Fixed LWIP_NETIF_TX_SINGLE_PBUF for LWIP_TCPIP_CORE_LOCKING
+
+  2010-03-06: Simon Goldschmidt
+  * ipv4/ip.c: Don't try to forward link-local addresses
+
+  2010-03-06: Simon Goldschmidt
+  * etharp.c: Fixed bug #29087: etharp: don't send packets for LinkLocal-
+    addresses to gw
+
+  2010-03-05: Simon Goldschmidt
+  * dhcp.c: Fixed bug #29072: Correctly set ciaddr based on message-type
+    and state.
+
+  2010-03-05: Simon Goldschmidt
+  * api_msg.c: Correctly set TCP_WRITE_FLAG_MORE when netconn_write is split
+    into multiple calls to tcp_write.    
+
+  2010-02-21: Simon Goldschmidt
+  * opt.h, mem.h, dns.c: task #10140: Remove DNS_USES_STATIC_BUF (keep
+    the implementation of DNS_USES_STATIC_BUF==1)
+
+  2010-02-20: Simon Goldschmidt
+  * tcp.h, tcp.c, tcp_in.c, tcp_out.c: Task #10088: Correctly implement
+    close() vs. shutdown(). Now the application does not get any more
+    recv callbacks after calling tcp_close(). Added tcp_shutdown().
+
+  2010-02-19: Simon Goldschmidt
+  * mem.c/.h, pbuf.c: Renamed mem_realloc() to mem_trim() to prevent
+    confusion with realloc()
+
+  2010-02-15: Simon Goldschmidt/Stephane Lesage
+  * netif.c/.h: Link status does not depend on LWIP_NETIF_LINK_CALLBACK
+    (fixes bug #28899)
+
+  2010-02-14: Simon Goldschmidt
+  * netif.c: Fixed bug #28877 (Duplicate ARP gratuitous packet with
+    LWIP_NETIF_LINK_CALLBACK set on) by only sending if both link- and
+    admin-status of a netif are up
+
+  2010-02-14: Simon Goldschmidt
+  * opt.h: Disable ETHARP_TRUST_IP_MAC by default since it slows down packet
+    reception and is not really necessary
+
+  2010-02-14: Simon Goldschmidt
+  * etharp.c/.h: Fixed ARP input processing: only add a new entry if a
+    request was directed as us (RFC 826, Packet Reception), otherwise
+    only update existing entries; internalized some functions
+
+  2010-02-14: Simon Goldschmidt
+  * netif.h, etharp.c, tcpip.c: Fixed bug #28183 (ARP and TCP/IP cannot be
+    disabled on netif used for PPPoE) by adding a new netif flag
+    (NETIF_FLAG_ETHERNET) that tells the stack the device is an ethernet
+    device but prevents usage of ARP (so that ethernet_input can be used
+    for PPPoE).
+
+  2010-02-12: Simon Goldschmidt
+  * netif.c: netif_set_link_up/down: only do something if the link state
+    actually changes
+
+  2010-02-12: Simon Goldschmidt/Stephane Lesage
+  * api_msg.c: Fixed bug #28865 (Cannot close socket/netconn in non-blocking
+    connect)
+
+  2010-02-12: Simon Goldschmidt
+  * mem.h: Fixed bug #28866 (mem_realloc function defined in mem.h)
+
+  2010-02-09: Simon Goldschmidt
+  * api_lib.c, api_msg.c, sockets.c, api.h, api_msg.h: Fixed bug #22110
+   (recv() makes receive window update for data that wasn't received by
+    application)
+
+  2010-02-09: Simon Goldschmidt/Stephane Lesage
+  * sockets.c: Fixed bug #28853 (lwip_recvfrom() returns 0 on receive time-out
+    or any netconn_recv() error)
+
+  2010-02-09: Simon Goldschmidt
+  * ppp.c: task #10154 (PPP: Update snmp in/out counters for tx/rx packets)
+
+  2010-02-09: Simon Goldschmidt
+  * netif.c: For loopback packets, adjust the stats- and snmp-counters
+    for the loopback netif.
+
+  2010-02-08: Simon Goldschmidt
+  * igmp.c/.h, ip.h: Moved most defines from igmp.h to igmp.c for clarity
+    since they are not used anywhere else.
+
+  2010-02-08: Simon Goldschmidt (Stéphane Lesage)
+  * igmp.c, igmp.h, stats.c, stats.h: Improved IGMP stats
+    (patch from bug #28798)
+
+  2010-02-08: Simon Goldschmidt (Stéphane Lesage)
+  * igmp.c: Fixed bug #28798 (Error in "Max Response Time" processing) and
+    another bug when LWIP_RAND() returns zero.
+
+  2010-02-04: Simon Goldschmidt
+  * nearly every file: Use macros defined in ip_addr.h (some of them new)
+    to work with IP addresses (preparation for bug #27352 - Change ip_addr
+    from struct to typedef (u32_t) - and better code).
+
+  2010-01-31: Simon Goldschmidt
+  * netif.c: Don't call the link-callback from netif_set_up/down() since
+    this invalidly retriggers DHCP.
+
+  2010-01-29: Simon Goldschmidt
+  * ip_addr.h, inet.h, def.h, inet.c, def.c, more: Cleanly separate the
+    portability file inet.h and its contents from the stack: moved htonX-
+    functions to def.h (and the new def.c - they are not ipv4 dependent),
+    let inet.h depend on ip_addr.h and not the other way round.
+    This fixes bug #28732.
+
+  2010-01-28: Kieran Mansley
+  * tcp.c: Ensure ssthresh >= 2*MSS
+
+  2010-01-27: Simon Goldschmidt
+  * tcp.h, tcp.c, tcp_in.c: Fixed bug #27871: Calling tcp_abort() in recv
+    callback can lead to accessing unallocated memory. As a consequence,
+    ERR_ABRT means the application has called tcp_abort()!
+
+  2010-01-25: Simon Goldschmidt
+  * snmp_structs.h, msg_in.c: Partly fixed bug #22070 (MIB_OBJECT_WRITE_ONLY
+    not implemented in SNMP): write-only or not-accessible are still
+    returned by getnext (though not by get)
+
+  2010-01-24: Simon Goldschmidt
+  * snmp: Renamed the private mib node from 'private' to 'mib_private' to
+    not use reserved C/C++ keywords
+
+  2010-01-23: Simon Goldschmidt
+  * sockets.c: Fixed bug #28716: select() returns 0 after waiting for less
+    than 1 ms
+
+  2010-01-21: Simon Goldschmidt
+  * tcp.c, api_msg.c: Fixed bug #28651 (tcp_connect: no callbacks called
+    if tcp_enqueue fails) both in raw- and netconn-API
+
+  2010-01-19: Simon Goldschmidt
+  * api_msg.c: Fixed bug #27316: netconn: Possible deadlock in err_tcp
+
+  2010-01-18: Iordan Neshev/Simon Goldschmidt
+  * src/netif/ppp: reorganised PPP sourcecode to 2.3.11 including some
+    bugfix backports from 2.4.x.
+
+  2010-01-18: Simon Goldschmidt
+  * mem.c: Fixed bug #28679: mem_realloc calculates mem_stats wrong
+
+  2010-01-17: Simon Goldschmidt
+  * api_lib.c, api_msg.c, (api_msg.h, api.h, sockets.c, tcpip.c):
+    task #10102: "netconn: clean up conn->err threading issues" by adding
+    error return value to struct api_msg_msg
+
+  2010-01-17: Simon Goldschmidt
+  * api.h, api_lib.c, sockets.c: Changed netconn_recv() and netconn_accept()
+    to return err_t (bugs #27709 and #28087)
+
+  2010-01-14: Simon Goldschmidt
+  * ...: Use typedef for function prototypes throughout the stack.
+
+  2010-01-13: Simon Goldschmidt
+  * api_msg.h/.c, api_lib.c: Fixed bug #26672 (close connection when receive
+    window = 0) by correctly draining recvmbox/acceptmbox
+
+  2010-01-11: Simon Goldschmidt
+  * pap.c: Fixed bug #13315 (PPP PAP authentication can result in
+    erroneous callbacks) by copying the code from recent pppd
+
+  2010-01-10: Simon Goldschmidt
+  * raw.c: Fixed bug #28506 (raw_bind should filter received packets)
+
+  2010-01-10: Simon Goldschmidt
+  * tcp.h/.c: bug #28127 (remove call to tcp_output() from tcp_ack(_now)())
+
+  2010-01-08: Simon Goldschmidt
+  * sockets.c: Fixed bug #28519 (lwip_recvfrom bug with len > 65535)
+
+  2010-01-08: Simon Goldschmidt
+  * dns.c: Copy hostname for DNS_LOCAL_HOSTLIST_IS_DYNAMIC==1 since string
+    passed to dns_local_addhost() might be volatile
+
+  2010-01-07: Simon Goldschmidt
+  * timers.c, tcp.h: Call tcp_timer_needed() with NO_SYS==1, too
+
+  2010-01-06: Simon Goldschmidt
+  * netdb.h: Fixed bug #28496: missing include guards in netdb.h
+
+  2009-12-31: Simon Goldschmidt
+  * many ppp files: Reorganised PPP source code from ucip structure to pppd
+    structure to easily compare our code against the pppd code (around v2.3.1)
+
+  2009-12-27: Simon Goldschmidt
+  * tcp_in.c: Another fix for bug #28241 (ooseq processing) and adapted
+    unit test
+
+
+(STABLE-1.3.2)
+
+  ++ New features:
+
+  2009-10-27 Simon Goldschmidt/Stephan Lesage
+  * netifapi.c/.h: Added netifapi_netif_set_addr()
+
+  2009-10-07 Simon Goldschmidt/Fabian Koch
+  * api_msg.c, netbuf.c/.h, opt.h: patch #6888: Patch for UDP Netbufs to
+    support dest-addr and dest-port (optional: LWIP_NETBUF_RECVINFO)
+
+  2009-08-26 Simon Goldschmidt/Simon Kallweit
+  * slipif.c/.h: bug #26397: SLIP polling support
+
+  2009-08-25 Simon Goldschmidt
+  * opt.h, etharp.h/.c: task #9033: Support IEEE 802.1q tagged frame (VLAN),
+    New configuration options ETHARP_SUPPORT_VLAN and ETHARP_VLAN_CHECK.
+
+  2009-08-25 Simon Goldschmidt
+  * ip_addr.h, netdb.c: patch #6900: added define ip_ntoa(struct ip_addr*)
+
+  2009-08-24 Jakob Stoklund Olesen
+  * autoip.c, dhcp.c, netif.c: patch #6725: Teach AutoIP and DHCP to respond
+    to netif_set_link_up().
+
+  2009-08-23 Simon Goldschmidt
+  * tcp.h/.c: Added function tcp_debug_state_str() to convert a tcp state
+    to a human-readable string.
+
+  ++ Bugfixes:
+
+  2009-12-24: Kieran Mansley
+  * tcp_in.c Apply patches from Oleg Tyshev to improve OOS processing
+    (BUG#28241)
+
+  2009-12-06: Simon Goldschmidt
+  * ppp.h/.c: Fixed bug #27079 (Yet another leak in PPP): outpacket_buf can
+    be statically allocated (like in ucip)
+
+  2009-12-04: Simon Goldschmidt (patch by Ioardan Neshev)
+  * pap.c: patch #6969: PPP: missing PAP authentication UNTIMEOUT
+
+  2009-12-03: Simon Goldschmidt
+  * tcp.h, tcp_in.c, tcp_out.c: Fixed bug #28106: dup ack for fast retransmit
+    could have non-zero length
+
+  2009-12-02: Simon Goldschmidt
+  * tcp_in.c: Fixed bug #27904: TCP sends too many ACKs: delay resetting
+    tcp_input_pcb until after calling the pcb's callbacks
+
+  2009-11-29: Simon Goldschmidt
+  * tcp_in.c: Fixed bug #28054: Two segments with FIN flag on the out-of-
+    sequence queue, also fixed PBUF_POOL leak in the out-of-sequence code
+
+  2009-11-29: Simon Goldschmidt
+  * pbuf.c: Fixed bug #28064: pbuf_alloc(PBUF_POOL) is not thread-safe by
+    queueing a call into tcpip_thread to free ooseq-bufs if the pool is empty
+
+  2009-11-26: Simon Goldschmidt
+  * tcp.h: Fixed bug #28098: Nagle can prevent fast retransmit from sending
+    segment
+
+  2009-11-26: Simon Goldschmidt
+  * tcp.h, sockets.c: Fixed bug #28099: API required to disable Nagle
+    algorithm at PCB level
+
+  2009-11-22: Simon Goldschmidt
+  * tcp_out.c: Fixed bug #27905: FIN isn't combined with data on unsent
+
+  2009-11-22: Simon Goldschmidt (suggested by Bill Auerbach)
+  * tcp.c: tcp_alloc: prevent increasing stats.err for MEMP_TCP_PCB when
+    reusing time-wait pcb
+
+  2009-11-20: Simon Goldschmidt (patch by Albert Bartel)
+  * sockets.c: Fixed bug #28062: Data received directly after accepting
+    does not wake up select
+
+  2009-11-11: Simon Goldschmidt
+  * netdb.h: Fixed bug #27994: incorrect define for freeaddrinfo(addrinfo)
+
+  2009-10-30: Simon Goldschmidt
+  * opt.h: Increased default value for TCP_MSS to 536, updated default
+    value for TCP_WND to 4*TCP_MSS to keep delayed ACK working.
+
+  2009-10-28: Kieran Mansley
+  * tcp_in.c, tcp_out.c, tcp.h: re-work the fast retransmission code
+    to follow algorithm from TCP/IP Illustrated
+
+  2009-10-27: Kieran Mansley
+  * tcp_in.c: fix BUG#27445: grow cwnd with every duplicate ACK
+
+  2009-10-25: Simon Goldschmidt
+  * tcp.h: bug-fix in the TCP_EVENT_RECV macro (has to call tcp_recved if
+    pcb->recv is NULL to keep rcv_wnd correct)
+
+  2009-10-25: Simon Goldschmidt
+  * tcp_in.c: Fixed bug #26251: RST process in TIME_WAIT TCP state
+
+  2009-10-23: Simon Goldschmidt (David Empson)
+  * tcp.c: Fixed bug #27783: Silly window avoidance for small window sizes
+
+  2009-10-21: Simon Goldschmidt
+  * tcp_in.c: Fixed bug #27215: TCP sent() callback gives leading and
+    trailing 1 byte len (SYN/FIN)
+
+  2009-10-21: Simon Goldschmidt
+  * tcp_out.c: Fixed bug #27315: zero window probe and FIN
+
+  2009-10-19: Simon Goldschmidt
+  * dhcp.c/.h: Minor code simplification (don't store received pbuf, change
+    conditional code to assert where applicable), check pbuf length before
+    testing for valid reply
+
+  2009-10-19: Simon Goldschmidt
+  * dhcp.c: Removed most calls to udp_connect since they aren't necessary
+    when using udp_sendto_if() - always stay connected to IP_ADDR_ANY.
+
+  2009-10-16: Simon Goldschmidt
+  * ip.c: Fixed bug #27390: Source IP check in ip_input() causes it to drop
+    valid DHCP packets -> allow 0.0.0.0 as source address when LWIP_DHCP is
+    enabled
+
+  2009-10-15: Simon Goldschmidt (Oleg Tyshev)
+  * tcp_in.c: Fixed bug #27329: dupacks by unidirectional data transmit
+
+  2009-10-15: Simon Goldschmidt
+  * api_lib.c: Fixed bug #27709: conn->err race condition on netconn_recv()
+    timeout
+
+  2009-10-15: Simon Goldschmidt
+  * autoip.c: Fixed bug #27704: autoip starts with wrong address
+    LWIP_AUTOIP_CREATE_SEED_ADDR() returned address in host byte order instead
+    of network byte order
+
+  2009-10-11 Simon Goldschmidt (Jörg Kesten)
+  * tcp_out.c: Fixed bug #27504: tcp_enqueue wrongly concatenates segments
+    which are not consecutive when retransmitting unacked segments
+
+  2009-10-09 Simon Goldschmidt
+  * opt.h: Fixed default values of some stats to only be enabled if used
+    Fixes bug #27338: sys_stats is defined when NO_SYS = 1
+
+  2009-08-30 Simon Goldschmidt
+  * ip.c: Fixed bug bug #27345: "ip_frag() does not use the LWIP_NETIF_LOOPBACK
+    function" by checking for loopback before calling ip_frag
+
+  2009-08-25 Simon Goldschmidt
+  * dhcp.c: fixed invalid dependency to etharp_query if DHCP_DOES_ARP_CHECK==0
+
+  2009-08-23 Simon Goldschmidt
+  * ppp.c: bug #27078: Possible memory leak in pppInit()
+
+  2009-08-23 Simon Goldschmidt
+  * netdb.c, dns.c: bug #26657: DNS, if host name is "localhost", result
+    is error.
+
+  2009-08-23 Simon Goldschmidt
+  * opt.h, init.c: bug #26649: TCP fails when TCP_MSS > TCP_SND_BUF
+    Fixed wrong parenthesis, added check in init.c
+
+  2009-08-23 Simon Goldschmidt
+  * ppp.c: bug #27266: wait-state debug message in pppMain occurs every ms
+
+  2009-08-23 Simon Goldschmidt
+  * many ppp files: bug #27267: Added include to string.h where needed
+
+  2009-08-23 Simon Goldschmidt
+  * tcp.h: patch #6843: tcp.h macro optimization patch (for little endian)
+
+
+(STABLE-1.3.1)
+
+  ++ New features:
+
+  2009-05-10 Simon Goldschmidt
+  * opt.h, sockets.c, pbuf.c, netbuf.h, pbuf.h: task #7013: Added option
+    LWIP_NETIF_TX_SINGLE_PBUF to try to create transmit packets from only
+    one pbuf to help MACs that don't support scatter-gather DMA.
+
+  2009-05-09 Simon Goldschmidt
+  * icmp.h, icmp.c: Shrinked ICMP code, added option to NOT check icoming
+    ECHO pbuf for size (just use it): LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN
+
+  2009-05-05 Simon Goldschmidt, Jakob Stoklund Olesen
+  * ip.h, ip.c: Added ip_current_netif() & ip_current_header() to receive
+    extended info about the currently received packet.
+
+  2009-04-27 Simon Goldschmidt
+  * sys.h: Made SYS_LIGHTWEIGHT_PROT and sys_now() work with NO_SYS=1
+
+  2009-04-25 Simon Goldschmidt
+  * mem.c, opt.h: Added option MEM_USE_POOLS_TRY_BIGGER_POOL to try the next
+    bigger malloc pool if one is empty (only usable with MEM_USE_POOLS).
+
+  2009-04-21 Simon Goldschmidt
+  * dns.c, init.c, dns.h, opt.h: task #7507, patch #6786: DNS supports static
+    hosts table. New configuration options DNS_LOCAL_HOSTLIST and
+    DNS_LOCAL_HOSTLIST_IS_DYNAMIC. Also, DNS_LOOKUP_LOCAL_EXTERN() can be defined
+    as an external function for lookup.
+
+  2009-04-15 Simon Goldschmidt
+  * dhcp.c: patch #6763: Global DHCP XID can be redefined to something more unique
+
+  2009-03-31 Kieran Mansley
+  * tcp.c, tcp_out.c, tcp_in.c, sys.h, tcp.h, opts.h: add support for
+    TCP timestamp options, off by default.  Rework tcp_enqueue() to
+    take option flags rather than specified option data
+
+  2009-02-18 Simon Goldschmidt
+  * cc.h: Added printf formatter for size_t: SZT_F
+
+  2009-02-16 Simon Goldschmidt (patch by Rishi Khan)
+  * icmp.c, opt.h: patch #6539: (configurable) response to broadcast- and multicast
+    pings
+
+  2009-02-12 Simon Goldschmidt
+  * init.h: Added LWIP_VERSION to get the current version of the stack
+
+  2009-02-11 Simon Goldschmidt (suggested by Gottfried Spitaler)
+  * opt.h, memp.h/.c: added MEMP_MEM_MALLOC to use mem_malloc/mem_free instead
+    of the pool allocator (can save code size with MEM_LIBC_MALLOC if libc-malloc
+    is otherwise used)
+
+  2009-01-28 Jonathan Larmour (suggested by Bill Bauerbach)
+  * ipv4/inet_chksum.c, ipv4/lwip/inet_chksum.h: inet_chksum_pseudo_partial()
+  is only used by UDPLITE at present, so conditionalise it.
+
+  2008-12-03 Simon Goldschmidt (base on patch from Luca Ceresoli)
+  * autoip.c: checked in (slightly modified) patch #6683: Customizable AUTOIP
+    "seed" address. This should reduce AUTOIP conflicts if
+    LWIP_AUTOIP_CREATE_SEED_ADDR is overridden.
+
+  2008-10-02 Jonathan Larmour and Rishi Khan
+  * sockets.c (lwip_accept): Return EWOULDBLOCK if would block on non-blocking
+    socket.
+
+  2008-06-30 Simon Goldschmidt
+  * mem.c, opt.h, stats.h: fixed bug #21433: Calling mem_free/pbuf_free from
+    interrupt context isn't safe: LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT allows
+    mem_free to run between mem_malloc iterations. Added illegal counter for
+    mem stats.
+
+  2008-06-27 Simon Goldschmidt
+  * stats.h/.c, some other files: patch #6483: stats module improvement:
+    Added defines to display each module's statistic individually, added stats
+    defines for MEM, MEMP and SYS modules, removed (unused) rexmit counter.
+
+  2008-06-17 Simon Goldschmidt
+  * err.h: patch #6459: Made err_t overridable to use a more efficient type
+    (define LWIP_ERR_T in cc.h)
+
+  2008-06-17 Simon Goldschmidt
+  * slipif.c: patch #6480: Added a configuration option for slipif for symmetry
+    to loopif
+
+  2008-06-17 Simon Goldschmidt (patch by Luca Ceresoli)
+  * netif.c, loopif.c, ip.c, netif.h, loopif.h, opt.h: Checked in slightly
+    modified version of patch # 6370: Moved loopif code to netif.c so that
+    loopback traffic is supported on all netifs (all local IPs).
+    Added option to limit loopback packets for each netifs.
+
+
+  ++ Bugfixes:
+  2009-08-12 Kieran Mansley
+  * tcp_in.c, tcp.c: Fix bug #27209: handle trimming of segments when
+    out of window or out of order properly
+
+  2009-08-12 Kieran Mansley
+  * tcp_in.c: Fix bug #27199: use snd_wl2 instead of snd_wl1
+
+  2009-07-28 Simon Goldschmidt
+  * mem.h: Fixed bug #27105: "realloc() cannot replace mem_realloc()"s
+
+  2009-07-27 Kieran Mansley
+  * api.h api_msg.h netdb.h sockets.h: add missing #include directives
+
+  2009-07-09 Kieran Mansley
+  * api_msg.c, sockets.c, api.h: BUG23240 use signed counters for
+    recv_avail and don't increment counters until message successfully
+    sent to mbox
+
+  2009-06-25 Kieran Mansley
+  * api_msg.c api.h: BUG26722: initialise netconn write variables 
+    in netconn_alloc
+
+  2009-06-25 Kieran Mansley
+  * tcp.h: BUG26879: set ret value in TCP_EVENT macros when function is not set
+
+  2009-06-25 Kieran Mansley
+  * tcp.c, tcp_in.c, tcp_out.c, tcp.h: BUG26301 and BUG26267: correct
+    simultaneous close behaviour, and make snd_nxt have the same meaning 
+    as in the RFCs.
+
+  2009-05-12 Simon Goldschmidt
+  * etharp.h, etharp.c, netif.c: fixed bug #26507: "Gratuitous ARP depends on
+    arp_table / uses etharp_query" by adding etharp_gratuitous()
+
+  2009-05-12 Simon Goldschmidt
+  * ip.h, ip.c, igmp.c: bug #26487: Added ip_output_if_opt that can add IP options
+    to the IP header (used by igmp_ip_output_if)
+
+  2009-05-06 Simon Goldschmidt
+  * inet_chksum.c: On little endian architectures, use LWIP_PLATFORM_HTONS (if
+    defined) for SWAP_BYTES_IN_WORD to speed up checksumming.
+
+  2009-05-05 Simon Goldschmidt
+  * sockets.c: bug #26405: Prematurely released semaphore causes lwip_select()
+    to crash
+
+  2009-05-04 Simon Goldschmidt
+  * init.c: snmp was not initialized in lwip_init()
+
+  2009-05-04 Frédéric Bernon
+  * dhcp.c, netbios.c: Changes if IP_SOF_BROADCAST is enabled.
+
+  2009-05-03 Simon Goldschmidt
+  * tcp.h: bug #26349: Nagle algorithm doesn't send although segment is full
+    (and unsent->next == NULL)
+
+  2009-05-02 Simon Goldschmidt
+  * tcpip.h, tcpip.c: fixed tcpip_untimeout (does not need the time, broken after
+    1.3.0 in CVS only) - fixes compilation of ppp_oe.c
+
+  2009-05-02 Simon Goldschmidt
+  * msg_in.c: fixed bug #25636: SNMPSET value is ignored for integer fields
+
+  2009-05-01 Simon Goldschmidt
+  * pap.c: bug #21680: PPP upap_rauthnak() drops legal NAK packets
+
+  2009-05-01 Simon Goldschmidt
+  * ppp.c: bug #24228: Memory corruption with PPP and DHCP
+
+  2009-04-29 Frédéric Bernon
+  * raw.c, udp.c, init.c, opt.h, ip.h, sockets.h: bug #26309: Implement the
+    SO(F)_BROADCAST filter for all API layers. Avoid the unindented reception
+    of broadcast packets even when this option wasn't set. Port maintainers
+    which want to enable this filter have to set IP_SOF_BROADCAST=1 in opt.h.
+    If you want this option also filter broadcast on recv operations, you also
+    have to set IP_SOF_BROADCAST_RECV=1 in opt.h.
+
+  2009-04-28 Simon Goldschmidt, Jakob Stoklund Olesen
+  * dhcp.c: patch #6721, bugs #25575, #25576: Some small fixes to DHCP and
+    DHCP/AUTOIP cooperation
+
+  2009-04-25 Simon Goldschmidt, Oleg Tyshev
+  * tcp_out.c: bug #24212: Deadlocked tcp_retransmit due to exceeded pcb->cwnd
+    Fixed by sorting the unsent and unacked queues (segments are inserted at the
+    right place in tcp_output and tcp_rexmit).
+
+  2009-04-25 Simon Goldschmidt
+  * memp.c, mem.c, memp.h, mem_std.h: bug #26213 "Problem with memory allocation
+    when debugging": memp_sizes contained the wrong sizes (including sanity
+    regions); memp pools for MEM_USE_POOLS were too small
+
+  2009-04-24 Simon Goldschmidt, Frédéric Bernon
+  * inet.c: patch #6765: Fix a small problem with the last changes (incorrect
+    behavior, with with ip address string not ended by a '\0', a space or a
+    end of line)
+
+  2009-04-19 Simon Goldschmidt
+  * rawapi.txt: Fixed bug #26069: Corrected documentation: if tcp_connect fails,
+    pcb->err is called, not pcb->connected (with an error code).
+
+  2009-04-19 Simon Goldschmidt
+  * tcp_out.c: Fixed bug #26236: "TCP options (timestamp) don't work with
+    no-copy-tcpwrite": deallocate option data, only concat segments with same flags
+
+  2009-04-19 Simon Goldschmidt
+  * tcp_out.c: Fixed bug #25094: "Zero-length pbuf" (options are now allocated
+    in the header pbuf, not the data pbuf)
+
+  2009-04-18 Simon Goldschmidt
+  * api_msg.c: fixed bug #25695: Segmentation fault in do_writemore()
+
+  2009-04-15 Simon Goldschmidt
+  * sockets.c: tried to fix bug #23559: lwip_recvfrom problem with tcp
+
+  2009-04-15 Simon Goldschmidt
+  * dhcp.c: task #9192: mem_free of dhcp->options_in and dhcp->msg_in
+
+  2009-04-15 Simon Goldschmidt
+  * ip.c, ip6.c, tcp_out.c, ip.h: patch #6808: Add a utility function
+    ip_hinted_output() (for smaller code mainly)
+
+  2009-04-15 Simon Goldschmidt
+  * inet.c: patch #6765: Supporting new line characters in inet_aton()
+
+  2009-04-15 Simon Goldschmidt
+  * dhcp.c: patch #6764: DHCP rebind and renew did not send hostnam option;
+    Converted constant OPTION_MAX_MSG_SIZE to netif->mtu, check if netif->mtu
+    is big enough in dhcp_start
+
+  2009-04-15 Simon Goldschmidt
+  * netbuf.c: bug #26027: netbuf_chain resulted in pbuf memory leak
+
+  2009-04-15 Simon Goldschmidt
+  * sockets.c, ppp.c: bug #25763: corrected 4 occurrences of SMEMCPY to MEMCPY
+
+  2009-04-15 Simon Goldschmidt
+  * sockets.c: bug #26121: set_errno can be overridden
+
+  2009-04-09 Kieran Mansley (patch from Luca Ceresoli <lucaceresoli>)
+  * init.c, opt.h: Patch#6774 TCP_QUEUE_OOSEQ breaks compilation when
+    LWIP_TCP==0
+
+  2009-04-09 Kieran Mansley (patch from Roy Lee <roylee17>)
+  * tcp.h: Patch#6802 Add do-while-clauses to those function like
+    macros in tcp.h
+
+  2009-03-31 Kieran Mansley
+  * tcp.c, tcp_in.c, tcp_out.c, tcp.h, opt.h: Rework the way window
+    updates are calculated and sent (BUG20515)
+
+  * tcp_in.c: cope with SYN packets received during established states,
+    and retransmission of initial SYN.
+
+  * tcp_out.c: set push bit correctly when tcp segments are merged
+
+  2009-03-27 Kieran Mansley
+  * tcp_out.c set window correctly on probes (correcting change made
+    yesterday)
+
+  2009-03-26 Kieran Mansley
+  * tcp.c, tcp_in.c, tcp.h: add tcp_abandon() to cope with dropping
+    connections where no reset required (bug #25622)
+
+  * tcp_out.c: set TCP_ACK flag on keepalive and zero window probes 
+    (bug #20779)
+
+  2009-02-18 Simon Goldschmidt (Jonathan Larmour and Bill Auerbach)
+  * ip_frag.c: patch #6528: the buffer used for IP_FRAG_USES_STATIC_BUF could be
+    too small depending on MEM_ALIGNMENT
+
+  2009-02-16 Simon Goldschmidt
+  * sockets.h/.c, api_*.h/.c: fixed arguments of socket functions to match the standard;
+    converted size argument of netconn_write to 'size_t'
+
+  2009-02-16 Simon Goldschmidt
+  * tcp.h, tcp.c: fixed bug #24440: TCP connection close problem on 64-bit host
+    by moving accept callback function pointer to TCP_PCB_COMMON
+
+  2009-02-12 Simon Goldschmidt
+  * dhcp.c: fixed bug #25345 (DHCPDECLINE is sent with "Maximum message size"
+    option)
+
+  2009-02-11 Simon Goldschmidt
+  * dhcp.c: fixed bug #24480 (releasing old udp_pdb and pbuf in dhcp_start)
+
+  2009-02-11 Simon Goldschmidt
+  * opt.h, api_msg.c: added configurable default valud for netconn->recv_bufsize:
+    RECV_BUFSIZE_DEFAULT (fixes bug #23726: pbuf pool exhaustion on slow recv())
+
+  2009-02-10 Simon Goldschmidt
+  * tcp.c: fixed bug #25467: Listen backlog is not reset on timeout in SYN_RCVD:
+    Accepts_pending is decrease on a corresponding listen pcb when a connection
+    in state SYN_RCVD is close.
+
+  2009-01-28 Jonathan Larmour
+  * pbuf.c: reclaim pbufs from TCP out-of-sequence segments if we run
+    out of pool pbufs.
+
+  2008-12-19 Simon Goldschmidt
+  * many files: patch #6699: fixed some warnings on platform where sizeof(int) == 2 
+
+  2008-12-10 Tamas Somogyi, Frédéric Bernon
+  * sockets.c: fixed bug #25051: lwip_recvfrom problem with udp: fromaddr and
+    port uses deleted netbuf.
+
+  2008-10-18 Simon Goldschmidt
+  * tcp_in.c: fixed bug ##24596: Vulnerability on faulty TCP options length
+    in tcp_parseopt
+
+  2008-10-15 Simon Goldschmidt
+  * ip_frag.c: fixed bug #24517: IP reassembly crashes on unaligned IP headers
+    by packing the struct ip_reass_helper.
+
+  2008-10-03 David Woodhouse, Jonathan Larmour
+  * etharp.c (etharp_arp_input): Fix type aliasing problem copying ip address.
+
+  2008-10-02 Jonathan Larmour
+  * dns.c: Hard-code structure sizes, to avoid issues on some compilers where
+    padding is included.
+
+  2008-09-30 Jonathan Larmour
+  * sockets.c (lwip_accept): check addr isn't NULL. If it's valid, do an
+    assertion check that addrlen isn't NULL.
+
+  2008-09-30 Jonathan Larmour
+  * tcp.c: Fix bug #24227, wrong error message in tcp_bind.
+
+  2008-08-26 Simon Goldschmidt
+  * inet.h, ip_addr.h: fixed bug #24132: Cross-dependency between ip_addr.h and
+    inet.h -> moved declaration of struct in_addr from ip_addr.h to inet.h
+
+  2008-08-14 Simon Goldschmidt
+  * api_msg.c: fixed bug #23847: do_close_internal references freed memory (when
+    tcp_close returns != ERR_OK)
+
+  2008-07-08 Frédéric Bernon
+  * stats.h: Fix some build bugs introduced with patch #6483 (missing some parameters
+    in macros, mainly if MEM_STATS=0 and MEMP_STATS=0).
+
+  2008-06-24 Jonathan Larmour
+  * tcp_in.c: Fix for bug #23693 as suggested by Art R. Ensure cseg is unused
+    if tcp_seg_copy fails.
+
+  2008-06-17 Simon Goldschmidt
+  * inet_chksum.c: Checked in some ideas of patch #6460 (loop optimizations)
+    and created defines for swapping bytes and folding u32 to u16.
+
+  2008-05-30 Kieran Mansley
+  * tcp_in.c Remove redundant "if" statement, and use real rcv_wnd
+    rather than rcv_ann_wnd when deciding if packets are in-window.
+    Contributed by <arasmussen@consultant.datasys.swri.edu>
+
+  2008-05-30 Kieran Mansley
+  * mem.h: Fix BUG#23254.  Change macro definition of mem_* to allow
+    passing as function pointers when MEM_LIBC_MALLOC is defined.
+
+  2008-05-09 Jonathan Larmour
+  * err.h, err.c, sockets.c: Fix bug #23119: Reorder timeout error code to
+    stop it being treated as a fatal error.
+
+  2008-04-15 Simon Goldschmidt
+  * dhcp.c: fixed bug #22804: dhcp_stop doesn't clear NETIF_FLAG_DHCP
+    (flag now cleared)
+
+  2008-03-27 Simon Goldschmidt
+  * mem.c, tcpip.c, tcpip.h, opt.h: fixed bug #21433 (Calling mem_free/pbuf_free
+    from interrupt context isn't safe): set LWIP_USE_HEAP_FROM_INTERRUPT to 1
+    in lwipopts.h or use pbuf_free_callback(p)/mem_free_callback(m) to free pbufs
+    or heap memory from interrupt context
+
+  2008-03-26 Simon Goldschmidt
+  * tcp_in.c, tcp.c: fixed bug #22249: division by zero could occur if a remote
+    host sent a zero mss as TCP option.
+
+
+(STABLE-1.3.0)
+
+  ++ New features:
+
+  2008-03-10 Jonathan Larmour
+  * inet_chksum.c: Allow choice of one of the sample algorithms to be
+    made from lwipopts.h. Fix comment on how to override LWIP_CHKSUM.
+
+  2008-01-22 Frédéric Bernon
+  * tcp.c, tcp_in.c, tcp.h, opt.h: Rename LWIP_CALCULATE_EFF_SEND_MSS in 
+    TCP_CALCULATE_EFF_SEND_MSS to have coherent TCP options names.
+
+  2008-01-14 Frédéric Bernon
+  * rawapi.txt, api_msg.c, tcp.c, tcp_in.c, tcp.h: changes for task #7675 "Enable
+    to refuse data on a TCP_EVENT_RECV call". Important, behavior changes for the
+    tcp_recv callback (see rawapi.txt).
+
+  2008-01-14 Frédéric Bernon, Marc Chaland
+  * ip.c: Integrate patch #6369" ip_input : checking before realloc".
+  
+  2008-01-12 Frédéric Bernon
+  * tcpip.h, tcpip.c, api.h, api_lib.c, api_msg.c, sockets.c: replace the field
+    netconn::sem per netconn::op_completed like suggested for the task #7490
+    "Add return value to sys_mbox_post".
+
+  2008-01-12 Frédéric Bernon
+  * api_msg.c, opt.h: replace DEFAULT_RECVMBOX_SIZE per DEFAULT_TCP_RECVMBOX_SIZE,
+    DEFAULT_UDP_RECVMBOX_SIZE and DEFAULT_RAW_RECVMBOX_SIZE (to optimize queues
+    sizes), like suggested for the task #7490 "Add return value to sys_mbox_post".
+
+  2008-01-10 Frédéric Bernon
+  * tcpip.h, tcpip.c: add tcpip_callback_with_block function for the task #7490
+    "Add return value to sys_mbox_post". tcpip_callback is always defined as
+    "blocking" ("block" parameter = 1).
+
+  2008-01-10 Frédéric Bernon
+  * tcpip.h, tcpip.c, api.h, api_lib.c, api_msg.c, sockets.c: replace the field
+    netconn::mbox (sys_mbox_t) per netconn::sem (sys_sem_t) for the task #7490
+    "Add return value to sys_mbox_post".
+
+  2008-01-05 Frédéric Bernon
+  * sys_arch.txt, api.h, api_lib.c, api_msg.h, api_msg.c, tcpip.c, sys.h, opt.h:
+    Introduce changes for task #7490 "Add return value to sys_mbox_post" with some
+    modifications in the sys_mbox api: sys_mbox_new take a "size" parameters which
+    indicate the number of pointers query by the mailbox. There is three defines
+    in opt.h to indicate sizes for tcpip::mbox, netconn::recvmbox, and for the 
+    netconn::acceptmbox. Port maintainers, you can decide to just add this new 
+    parameter in your implementation, but to ignore it to keep the previous behavior.
+    The new sys_mbox_trypost function return a value to know if the mailbox is
+    full or if the message is posted. Take a look to sys_arch.txt for more details.
+    This new function is used in tcpip_input (so, can be called in an interrupt
+    context since the function is not blocking), and in recv_udp and recv_raw.
+
+  2008-01-04 Frédéric Bernon, Simon Goldschmidt, Jonathan Larmour
+  * rawapi.txt, api.h, api_lib.c, api_msg.h, api_msg.c, sockets.c, tcp.h, tcp.c,
+    tcp_in.c, init.c, opt.h: rename backlog options with TCP_ prefix, limit the
+    "backlog" parameter in an u8_t, 0 is interpreted as "smallest queue", add
+    documentation in the rawapi.txt file.
+
+  2007-12-31 Kieran Mansley (based on patch from Per-Henrik Lundbolm)
+  * tcp.c, tcp_in.c, tcp_out.c, tcp.h: Add TCP persist timer
+
+  2007-12-31 Frédéric Bernon, Luca Ceresoli
+  * autoip.c, etharp.c: ip_addr.h: Integrate patch #6348: "Broadcast ARP packets
+    in autoip". The change in etharp_raw could be removed, since all calls to
+    etharp_raw use ethbroadcast for the "ethdst_addr" parameter. But it could be
+    wrong in the future.
+
+  2007-12-30 Frédéric Bernon, Tom Evans
+  * ip.c: Fix bug #21846 "LwIP doesn't appear to perform any IP Source Address
+    Filtering" reported by Tom Evans.
+
+  2007-12-21 Frédéric Bernon, Simon Goldschmidt, Jonathan Larmour
+  * tcp.h, opt.h, api.h, api_msg.h, tcp.c, tcp_in.c, api_lib.c, api_msg.c,
+    sockets.c, init.c: task #7252: Implement TCP listen backlog: Warning: raw API
+    applications have to call 'tcp_accepted(pcb)' in their accept callback to
+    keep accepting new connections.
+
+  2007-12-13 Frédéric Bernon
+  * api_msg.c, err.h, err.c, sockets.c, dns.c, dns.h: replace "enum dns_result"
+    by err_t type. Add a new err_t code "ERR_INPROGRESS".
+
+  2007-12-12 Frédéric Bernon
+  * dns.h, dns.c, opt.h: move DNS options to the "right" place. Most visibles
+    are the one which have ram usage.
+
+  2007-12-05 Frédéric Bernon
+  * netdb.c: add a LWIP_DNS_API_HOSTENT_STORAGE option to decide to use a static
+    set of variables (=0) or a local one (=1). In this last case, your port should
+    provide a function "struct hostent* sys_thread_hostent( struct hostent* h)"
+    which have to do a copy of "h" and return a pointer ont the "per-thread" copy.
+
+  2007-12-03 Simon Goldschmidt
+  * ip.c: ip_input: check if a packet is for inp first before checking all other
+    netifs on netif_list (speeds up packet receiving in most cases)
+
+  2007-11-30 Simon Goldschmidt
+  * udp.c, raw.c: task #7497: Sort lists (pcb, netif, ...) for faster access
+    UDP: move a (connected) pcb selected for input to the front of the list of
+    pcbs so that it is found faster next time. Same for RAW pcbs that have eaten
+    a packet.
+
+  2007-11-28 Simon Goldschmidt
+  * etharp.c, stats.c, stats.h, opt.h: Introduced ETHARP_STATS
+
+  2007-11-25 Simon Goldschmidt
+  * dhcp.c: dhcp_unfold_reply() uses pbuf_copy_partial instead of its own copy
+    algorithm.
+
+  2007-11-24 Simon Goldschmidt
+  * netdb.h, netdb.c, sockets.h/.c: Moved lwip_gethostbyname from sockets.c
+    to the new file netdb.c; included lwip_getaddrinfo.
+
+  2007-11-21 Simon Goldschmidt
+  * tcp.h, opt.h, tcp.c, tcp_in.c: implemented calculating the effective send-mss
+    based on the MTU of the netif used to send. Enabled by default. Disable by
+    setting LWIP_CALCULATE_EFF_SEND_MSS to 0. This fixes bug #21492.
+
+  2007-11-19 Frédéric Bernon
+  * api_msg.c, dns.h, dns.c: Implement DNS_DOES_NAME_CHECK option (check if name
+    received match the name query), implement DNS_USES_STATIC_BUF (the place where
+    copy dns payload to parse the response), return an error if there is no place
+    for a new query, and fix some minor problems.
+
+  2007-11-16 Simon Goldschmidt
+  * new files: ipv4/inet.c, ipv4/inet_chksum.c, ipv6/inet6.c
+    removed files: core/inet.c, core/inet6.c
+    Moved inet files into ipv4/ipv6 directory; splitted inet.c/inet.h into
+    inet and chksum part; changed includes in all lwIP files as appropriate
+
+  2007-11-16 Simon Goldschmidt
+  * api.h, api_msg.h, api_lib.c, api_msg.c, socket.h, socket.c: Added sequential
+    dns resolver function for netconn api (netconn_gethostbyname) and socket api
+    (gethostbyname/gethostbyname_r).
+
+  2007-11-15 Jim Pettinato, Frédéric Bernon
+  * opt.h, init.c, tcpip.c, dhcp.c, dns.h, dns.c: add DNS client for simple name
+    requests with RAW api interface. Initialization is done in lwip_init() with
+    build time options. DNS timer is added in tcpip_thread context. DHCP can set
+    DNS server ip addresses when options are received. You need to set LWIP_DNS=1
+    in your lwipopts.h file (LWIP_DNS=0 in opt.h). DNS_DEBUG can be set to get
+    some traces with LWIP_DEBUGF. Sanity check have been added. There is a "todo"
+    list with points to improve.
+
+  2007-11-06 Simon Goldschmidt
+  * opt.h, mib2.c: Patch #6215: added ifAdminStatus write support (if explicitly
+    enabled by defining SNMP_SAFE_REQUESTS to 0); added code to check link status
+    for ifOperStatus if LWIP_NETIF_LINK_CALLBACK is defined.
+
+  2007-11-06 Simon Goldschmidt
+  * api.h, api_msg.h and dependent files: Task #7410: Removed the need to include
+    core header files in api.h (ip/tcp/udp/raw.h) to hide the internal
+    implementation from netconn api applications.
+
+  2007-11-03 Frédéric Bernon
+  * api.h, api_lib.c, api_msg.c, sockets.c, opt.h: add SO_RCVBUF option for UDP &
+    RAW netconn. You need to set LWIP_SO_RCVBUF=1 in your lwipopts.h (it's disabled
+    by default). Netconn API users can use the netconn_recv_bufsize macro to access
+    it. This is a first release which have to be improve for TCP. Note it used the
+    netconn::recv_avail which need to be more "thread-safe" (note there is already
+    the problem for FIONREAD with lwip_ioctl/ioctlsocket).
+
+  2007-11-01 Frédéric Bernon, Marc Chaland
+  * sockets.h, sockets.c, api.h, api_lib.c, api_msg.h, api_msg.c, tcp.h, tcp_out.c:
+    Integrate "patch #6250 : MSG_MORE flag for send". MSG_MORE is used at socket api
+    layer, NETCONN_MORE at netconn api layer, and TCP_WRITE_FLAG_MORE at raw api
+    layer. This option enable to delayed TCP PUSH flag on multiple "write" calls.
+    Note that previous "copy" parameter for "write" APIs is now called "apiflags".
+
+  2007-10-24 Frédéric Bernon
+  * api.h, api_lib.c, api_msg.c: Add macro API_EVENT in the same spirit than 
+    TCP_EVENT_xxx macros to get a code more readable. It could also help to remove
+    some code (like we have talk in "patch #5919 : Create compile switch to remove
+    select code"), but it could be done later.
+
+  2007-10-08 Simon Goldschmidt
+  * many files: Changed initialization: many init functions are not needed any
+    more since we now rely on the compiler initializing global and static
+    variables to zero!
+
+  2007-10-06 Simon Goldschmidt
+  * ip_frag.c, memp.c, mib2.c, ip_frag.h, memp_std.h, opt.h: Changed IP_REASSEMBLY
+    to enqueue the received pbufs so that multiple packets can be reassembled
+    simultaneously and no static reassembly buffer is needed.
+
+  2007-10-05 Simon Goldschmidt
+  * tcpip.c, etharp.h, etharp.c: moved ethernet_input from tcpip.c to etharp.c so
+    all netifs (or ports) can use it.
+
+  2007-10-05 Frédéric Bernon
+  * netifapi.h, netifapi.c: add function netifapi_netif_set_default. Change the 
+    common function to reduce a little bit the footprint (for all functions using
+    only the "netif" parameter).
+
+  2007-10-03 Frédéric Bernon
+  * netifapi.h, netifapi.c: add functions netifapi_netif_set_up, netifapi_netif_set_down,
+    netifapi_autoip_start and netifapi_autoip_stop. Use a common function to reduce
+    a little bit the footprint (for all functions using only the "netif" parameter).
+
+  2007-09-15 Frédéric Bernon
+  * udp.h, udp.c, sockets.c: Changes for "#20503 IGMP Improvement". Add IP_MULTICAST_IF
+    option in socket API, and a new field "multicast_ip" in "struct udp_pcb" (for
+    netconn and raw API users), only if LWIP_IGMP=1. Add getsockopt processing for
+    IP_MULTICAST_TTL and IP_MULTICAST_IF.
+
+  2007-09-10 Frédéric Bernon
+  * snmp.h, mib2.c: enable to remove SNMP timer (which consumne several cycles
+    even when it's not necessary). snmp_agent.txt tell to call snmp_inc_sysuptime()
+    each 10ms (but, it's intrusive if you use sys_timeout feature). Now, you can
+    decide to call snmp_add_sysuptime(100) each 1000ms (which is bigger "step", but
+    call to a lower frequency). Or, you can decide to not call snmp_inc_sysuptime()
+    or snmp_add_sysuptime(), and to define the SNMP_GET_SYSUPTIME(sysuptime) macro.
+    This one is undefined by default in mib2.c. SNMP_GET_SYSUPTIME is called inside
+    snmp_get_sysuptime(u32_t *value), and enable to change "sysuptime" value only
+    when it's queried (any direct call to "sysuptime" is changed by a call to 
+    snmp_get_sysuptime).
+
+  2007-09-09 Frédéric Bernon, Bill Florac
+  * igmp.h, igmp.c, netif.h, netif.c, ip.c: To enable to have interfaces with IGMP,
+    and others without it, there is a new NETIF_FLAG_IGMP flag to set in netif->flags
+    if you want IGMP on an interface. igmp_stop() is now called inside netif_remove().
+    igmp_report_groups() is now called inside netif_set_link_up() (need to have
+    LWIP_NETIF_LINK_CALLBACK=1) to resend reports once the link is up (avoid to wait
+    the next query message to receive the matching multicast streams).
+
+  2007-09-08 Frédéric Bernon
+  * sockets.c, ip.h, api.h, tcp.h: declare a "struct ip_pcb" which only contains
+    IP_PCB. Add in the netconn's "pcb" union a "struct ip_pcb *ip;" (no size change).
+    Use this new field to access to common pcb fields (ttl, tos, so_options, etc...).
+    Enable to access to these fields with LWIP_TCP=0.
+
+  2007-09-05 Frédéric Bernon
+  * udp.c, ipv4/icmp.c, ipv4/ip.c, ipv6/icmp.c, ipv6/ip6.c, ipv4/icmp.h,
+    ipv6/icmp.h, opt.h: Integrate "task #7272 : LWIP_ICMP option". The new option
+    LWIP_ICMP enable/disable ICMP module inside the IP stack (enable per default).
+    Be careful, disabling ICMP make your product non-compliant to RFC1122, but
+    help to reduce footprint, and to reduce "visibility" on the Internet.
+
+  2007-09-05 Frédéric Bernon, Bill Florac
+  * opt.h, sys.h, tcpip.c, slipif.c, ppp.c, sys_arch.txt: Change parameters list
+    for sys_thread_new (see "task #7252 : Create sys_thread_new_ex()"). Two new
+    parameters have to be provided: a task name, and a task stack size. For this
+    one, since it's platform dependant, you could define the best one for you in
+    your lwipopts.h. For port maintainers, you can just add these new parameters
+    in your sys_arch.c file, and but it's not mandatory, use them in your OS
+    specific functions.
+
+  2007-09-05 Frédéric Bernon
+  * inet.c, autoip.c, msg_in.c, msg_out.c, init.c: Move some build time checkings
+    inside init.c for task #7142 "Sanity check user-configurable values".
+
+  2007-09-04 Frédéric Bernon, Bill Florac
+  * igmp.h, igmp.c, memp_std.h, memp.c, init.c, opt.h: Replace mem_malloc call by
+    memp_malloc, and use a new MEMP_NUM_IGMP_GROUP option (see opt.h to define the
+    value). It will avoid potential fragmentation problems, use a counter to know
+    how many times a group is used on an netif, and free it when all applications
+    leave it. MEMP_NUM_IGMP_GROUP got 8 as default value (and init.c got a sanity
+    check if LWIP_IGMP!=0).
+
+  2007-09-03 Frédéric Bernon
+  * igmp.h, igmp.c, sockets.c, api_msg.c: Changes for "#20503 IGMP Improvement".
+    Initialize igmp_mac_filter to NULL in netif_add (this field should be set in
+    the netif's "init" function). Use the "imr_interface" field (for socket layer)
+    and/or the "interface" field (for netconn layer), for join/leave operations.
+    The igmp_join/leavegroup first parameter change from a netif to an ipaddr.
+    This field could be a netif's ipaddr, or "any" (same meaning than ip_addr_isany).
+
+  2007-08-30 Frédéric Bernon
+  * Add netbuf.h, netbuf.c, Change api.h, api_lib.c: #7249 "Split netbuf functions
+    from api/api_lib". Now netbuf API is independant of netconn, and can be used
+    with other API (application based on raw API, or future "socket2" API). Ports
+    maintainers just have to add src/api/netbuf.c in their makefile/projects.
+
+  2007-08-30 Frédéric Bernon, Jonathan Larmour
+  * init.c: Add first version of lwip_sanity_check for task #7142 "Sanity check
+    user-configurable values".
+
+  2007-08-29 Frédéric Bernon
+  * igmp.h, igmp.c, tcpip.c, init.c, netif.c: change igmp_init and add igmp_start.
+    igmp_start is call inside netif_add. Now, igmp initialization is in the same
+    spirit than the others modules. Modify some IGMP debug traces.
+
+  2007-08-29 Frédéric Bernon
+  * Add init.h, init.c, Change opt.h, tcpip.c: Task  #7213 "Add a lwip_init function"
+    Add lwip_init function to regroup all modules initializations, and to provide
+    a place to add code for task #7142 "Sanity check user-configurable values".
+    Ports maintainers should remove direct initializations calls from their code,
+    and add init.c in their makefiles. Note that lwip_init() function is called
+    inside tcpip_init, but can also be used by raw api users since all calls are
+    disabled when matching options are disabled. Also note that their is new options
+    in opt.h, you should configure in your lwipopts.h (they are enabled per default).
+
+  2007-08-26 Marc Boucher
+  * api_msg.c: do_close_internal(): Reset the callbacks and arg (conn) to NULL
+    since they can under certain circumstances be called with an invalid conn
+    pointer after the connection has been closed (and conn has been freed). 
+
+  2007-08-25 Frédéric Bernon (Artem Migaev's Patch)
+  * netif.h, netif.c: Integrate "patch #6163 : Function to check if link layer is up".
+    Add a netif_is_link_up() function if LWIP_NETIF_LINK_CALLBACK option is set.
+
+  2007-08-22 Frédéric Bernon
+  * netif.h, netif.c, opt.h: Rename LWIP_NETIF_CALLBACK in LWIP_NETIF_STATUS_CALLBACK
+    to be coherent with new LWIP_NETIF_LINK_CALLBACK option before next release.
+
+  2007-08-22 Frédéric Bernon
+  * tcpip.h, tcpip.c, ethernetif.c, opt.h: remove options ETHARP_TCPIP_INPUT &
+    ETHARP_TCPIP_ETHINPUT, now, only "ethinput" code is supported, even if the 
+    name is tcpip_input (we keep the name of 1.2.0 function).
+
+  2007-08-17 Jared Grubb
+  * memp_std.h, memp.h, memp.c, mem.c, stats.c: (Task #7136) Centralize mempool 
+    settings into new memp_std.h and optional user file lwippools.h. This adds
+    more dynamic mempools, and allows the user to create an arbitrary number of
+    mempools for mem_malloc.
+
+  2007-08-16 Marc Boucher
+  * api_msg.c: Initialize newconn->state to NETCONN_NONE in accept_function;
+    otherwise it was left to NETCONN_CLOSE and sent_tcp() could prematurely
+    close the connection.
+
+  2007-08-16 Marc Boucher
+  * sockets.c: lwip_accept(): check netconn_peer() error return.
+
+  2007-08-16 Marc Boucher
+  * mem.c, mem.h: Added mem_calloc().
+
+  2007-08-16 Marc Boucher
+  * tcpip.c, tcpip.h memp.c, memp.h: Added distinct memp (MEMP_TCPIP_MSG_INPKT)
+    for input packets to prevent floods from consuming all of MEMP_TCPIP_MSG
+    and starving other message types.
+    Renamed MEMP_TCPIP_MSG to MEMP_TCPIP_MSG_API
+
+  2007-08-16 Marc Boucher
+  * pbuf.c, pbuf.h, etharp.c, tcp_in.c, sockets.c: Split pbuf flags in pbuf
+    type and flgs (later renamed to flags).
+    Use enum pbuf_flag as pbuf_type.  Renumber PBUF_FLAG_*.
+    Improved lwip_recvfrom().  TCP push now propagated.
+
+  2007-08-16 Marc Boucher
+  * ethernetif.c, contrib/ports/various: ethbroadcast now a shared global
+    provided by etharp.
+
+  2007-08-16 Marc Boucher
+  * ppp_oe.c ppp_oe.h, auth.c chap.c fsm.c lcp.c ppp.c ppp.h,
+    etharp.c ethernetif.c, etharp.h, opt.h tcpip.h, tcpip.c:
+    Added PPPoE support and various PPP improvements.
+
+  2007-07-25 Simon Goldschmidt
+  * api_lib.c, ip_frag.c, pbuf.c, api.h, pbuf.h: Introduced pbuf_copy_partial,
+    making netbuf_copy_partial use this function.
+
+  2007-07-25 Simon Goldschmidt
+  * tcp_in.c: Fix bug #20506: Slow start / initial congestion window starts with
+    2 * mss (instead of 1 * mss previously) to comply with some newer RFCs and
+    other stacks.
+
+  2007-07-13 Jared Grubb (integrated by Frédéric Bernon)
+  * opt.h, netif.h, netif.c, ethernetif.c: Add new configuration option to add
+    a link callback in the netif struct, and functions to handle it. Be carefull
+    for port maintainers to add the NETIF_FLAG_LINK_UP flag (like in ethernetif.c)
+    if you want to be sure to be compatible with future changes...
+
+  2007-06-30 Frédéric Bernon
+  * sockets.h, sockets.c: Implement MSG_PEEK flag for recv/recvfrom functions.
+
+  2007-06-21 Simon Goldschmidt
+  * etharp.h, etharp.c: Combined etharp_request with etharp_raw for both
+    LWIP_AUTOIP =0 and =1 to remove redundant code.
+
+  2007-06-21 Simon Goldschmidt
+  * mem.c, memp.c, mem.h, memp.h, opt.h: task #6863: Introduced the option
+    MEM_USE_POOLS to use 4 pools with different sized elements instead of a
+    heap. This both prevents memory fragmentation and gives a higher speed
+    at the cost of more memory consumption. Turned off by default.
+
+  2007-06-21 Simon Goldschmidt
+  * api_lib.c, api_msg.c, api.h, api_msg.h: Converted the length argument of
+    netconn_write (and therefore also api_msg_msg.msg.w.len) from u16_t into
+    int to be able to send a bigger buffer than 64K with one time (mainly
+    used from lwip_send).
+
+  2007-06-21 Simon Goldschmidt
+  * tcp.h, api_msg.c: Moved the nagle algorithm from netconn_write/do_write
+    into a define (tcp_output_nagle) in tcp.h to provide it to raw api users, too.
+
+  2007-06-21 Simon Goldschmidt
+  * api.h, api_lib.c, api_msg.c: Fixed bug #20021: Moved sendbuf-processing in
+    netconn_write from api_lib.c to api_msg.c to also prevent multiple context-
+    changes on low memory or empty send-buffer.
+
+  2007-06-18 Simon Goldschmidt
+  * etharp.c, etharp.h: Changed etharp to use a defined hardware address length
+    of 6 to avoid loading netif->hwaddr_len every time (since this file is only
+    used for ethernet and struct eth_addr already had a defined length of 6).
+
+  2007-06-17 Simon Goldschmidt
+  * sockets.c, sockets.h: Implemented socket options SO_NO_CHECK for UDP sockets
+    to disable UDP checksum generation on transmit.
+
+  2007-06-13 Frédéric Bernon, Simon Goldschmidt
+  * debug.h, api_msg.c: change LWIP_ERROR to use it to check errors like invalid
+    pointers or parameters, and let the possibility to redefined it in cc.h. Use
+    this macro to check "conn" parameter in api_msg.c functions.
+
+  2007-06-11 Simon Goldschmidt
+  * sockets.c, sockets.h: Added UDP lite support for sockets
+
+  2007-06-10 Simon Goldschmidt
+  * udp.h, opt.h, api_msg.c, ip.c, udp.c: Included switch LWIP_UDPLITE (enabled
+    by default) to switch off UDP-Lite support if not needed (reduces udp.c code
+    size)
+
+  2007-06-09 Dominik Spies (integrated by Frédéric Bernon)
+  * autoip.h, autoip.c, dhcp.h, dhcp.c, netif.h, netif.c, etharp.h, etharp.c, opt.h:
+    AutoIP implementation available for IPv4, with new options LWIP_AUTOIP and
+    LWIP_DHCP_AUTOIP_COOP if you want to cooperate with DHCP. Some tips to adapt
+    (see TODO mark in the source code).
+
+  2007-06-09 Simon Goldschmidt
+  * etharp.h, etharp.c, ethernetif.c: Modified order of parameters for
+    etharp_output() to match netif->output so etharp_output() can be used
+    directly as netif->output to save one function call.
+
+  2007-06-08 Simon Goldschmidt
+  * netif.h, ethernetif.c, slipif.c, loopif.c: Added define
+    NETIF_INIT_SNMP(netif, type, speed) to initialize per-netif snmp variables,
+    added initialization of those to ethernetif, slipif and loopif.
+
+  2007-05-18 Simon Goldschmidt
+  * opt.h, ip_frag.c, ip_frag.h, ip.c: Added option IP_FRAG_USES_STATIC_BUF
+    (defaulting to off for now) that can be set to 0 to send fragmented
+    packets by passing PBUF_REFs down the stack.
+
+  2007-05-23 Frédéric Bernon
+  * api_lib.c: Implement SO_RCVTIMEO for accept and recv on TCP
+    connections, such present in patch #5959.
+
+  2007-05-23 Frédéric Bernon
+  * api.h, api_lib.c, api_msg.c, sockets.c: group the different NETCONN_UDPxxx
+    code in only one part...
+
+  2007-05-18 Simon Goldschmidt
+  * opt.h, memp.h, memp.c: Added option MEMP_OVERFLOW_CHECK to check for memp
+    elements to overflow. This is achieved by adding some bytes before and after
+    each pool element (increasing their size, of course), filling them with a
+    prominent value and checking them on freeing the element.
+    Set it to 2 to also check every element in every pool each time memp_malloc()
+    or memp_free() is called (slower but more helpful).
+
+  2007-05-10 Simon Goldschmidt
+  * opt.h, memp.h, memp.c, pbuf.c (see task #6831): use a new memp pool for
+    PBUF_POOL pbufs instead of the old pool implementation in pbuf.c to reduce
+    code size.
+
+  2007-05-11 Frédéric Bernon
+  * sockets.c, api_lib.c, api_msg.h, api_msg.c, netifapi.h, netifapi.c, tcpip.c:
+    Include a function pointer instead of a table index in the message to reduce
+    footprint. Disable some part of lwip_send and lwip_sendto if some options are
+    not set (LWIP_TCP, LWIP_UDP, LWIP_RAW).
+
+  2007-05-10 Simon Goldschmidt
+  * *.h (except netif/ppp/*.h): Included patch #5448: include '#ifdef __cplusplus
+    \ extern "C" {' in all header files. Now you can write your application using
+    the lwIP stack in C++ and simply #include the core files. Note I have left
+    out the netif/ppp/*h header files for now, since I don't know which files are
+    included by applications and which are for internal use only.
+
+  2007-05-09 Simon Goldschmidt
+  * opt.h, *.c/*.h: Included patch #5920: Create define to override C-library
+    memcpy. 2 Defines are created: MEMCPY() for normal memcpy, SMEMCPY() for
+    situations where some compilers might inline the copy and save a function
+    call. Also replaced all calls to memcpy() with calls to (S)MEMCPY().
+
+  2007-05-08 Simon Goldschmidt
+  * mem.h: If MEM_LIBC_MALLOC==1, allow the defines (e.g. mem_malloc() -> malloc())
+    to be overriden in case the C-library malloc implementation is not protected
+    against concurrent access.
+
+  2007-05-04 Simon Goldschmidt (Atte Kojo)
+  * etharp.c: Introduced fast one-entry-cache to speed up ARP lookup when sending
+    multiple packets to the same host.
+
+  2007-05-04 Frédéric Bernon, Jonathan Larmour
+  * sockets.c, api.h, api_lib.c, api_msg.h, api_msg.c: Fix bug #19162 "lwip_sento: a possible
+    to corrupt remote addr/port connection state". Reduce problems "not enought memory" with
+    netbuf (if we receive lot of datagrams). Improve lwip_sendto (only one exchange between
+    sockets api and api_msg which run in tcpip_thread context). Add netconn_sento function.
+    Warning, if you directly access to "fromaddr" & "fromport" field from netbuf struct,
+    these fields are now renamed "addr" & "port".
+
+  2007-04-11 Jonathan Larmour
+  * sys.h, api_lib.c: Provide new sys_mbox_tryfetch function. Require ports to provide new
+    sys_arch_mbox_tryfetch function to get a message if one is there, otherwise return
+    with SYS_MBOX_EMPTY. sys_arch_mbox_tryfetch can be implemented as a function-like macro
+    by the port in sys_arch.h if desired.
+
+  2007-04-06 Frédéric Bernon, Simon Goldschmidt
+  * opt.h, tcpip.h, tcpip.c, netifapi.h, netifapi.c: New configuration option LWIP_NETIF_API
+    allow to use thread-safe functions to add/remove netif in list, and to start/stop dhcp
+    clients, using new functions from netifapi.h. Disable as default (no port change to do).
+
+  2007-04-05 Frédéric Bernon
+  * sockets.c: remplace ENOBUFS errors on alloc_socket by ENFILE to be more BSD compliant.
+
+  2007-04-04 Simon Goldschmidt
+  * arch.h, api_msg.c, dhcp.c, msg_in.c, sockets.c: Introduced #define LWIP_UNUSED_ARG(x)
+    use this for and architecture-independent form to tell the compiler you intentionally
+    are not using this variable. Can be overriden in cc.h.
+
+  2007-03-28 Frédéric Bernon
+  * opt.h, netif.h, dhcp.h, dhcp.c: New configuration option LWIP_NETIF_HOSTNAME allow to
+    define a hostname in netif struct (this is just a pointer, so, you can use a hardcoded
+    string, point on one of your's ethernetif field, or alloc a string you will free yourself).
+    It will be used by DHCP to register a client hostname, but can also be use when you call
+    snmp_set_sysname.
+
+  2007-03-28 Frédéric Bernon
+  * netif.h, netif.c: A new NETIF_FLAG_ETHARP flag is defined in netif.h, to allow to 
+    initialize a network interface's flag with. It tell this interface is an ethernet
+    device, and we can use ARP with it to do a "gratuitous ARP" (RFC 3220 "IP Mobility
+    Support for IPv4" section 4.6) when interface is "up" with netif_set_up().
+
+  2007-03-26 Frédéric Bernon, Jonathan Larmour
+  * opt.h, tcpip.c: New configuration option LWIP_ARP allow to disable ARP init at build
+    time if you only use PPP or SLIP. The default is enable. Note we don't have to call 
+    etharp_init in your port's initilization sequence if you use tcpip.c, because this call
+    is done in tcpip_init function.
+
+  2007-03-22 Frédéric Bernon
+  * stats.h, stats.c, msg_in.c: Stats counters can be change to u32_t if necessary with the
+    new option LWIP_STATS_LARGE. If you need this option, define LWIP_STATS_LARGE to 1 in
+    your lwipopts.h. More, unused counters are not defined in the stats structs, and not 
+    display by stats_display(). Note that some options (SYS_STATS and RAW_STATS) are defined
+    but never used. Fix msg_in.c with the correct #if test for a stat display.
+
+  2007-03-21 Kieran Mansley
+  * netif.c, netif.h: Apply patch#4197 with some changes (originator: rireland@hmgsl.com). 
+    Provides callback on netif up/down state change.
+
+  2007-03-11 Frédéric Bernon, Mace Gael, Steve Reynolds
+  * sockets.h, sockets.c, api.h, api_lib.c, api_msg.h, api_msg.c, igmp.h, igmp.c,
+    ip.c, netif.h, tcpip.c, opt.h:
+    New configuration option LWIP_IGMP to enable IGMP processing. Based on only one 
+    filter per all network interfaces. Declare a new function in netif to enable to
+    control the MAC filter (to reduce lwIP traffic processing).
+
+  2007-03-11 Frédéric Bernon
+  * tcp.h, tcp.c, sockets.c, tcp_out.c, tcp_in.c, opt.h: Keepalive values can
+    be configured at run time with LWIP_TCP_KEEPALIVE, but don't change this
+    unless you know what you're doing (default are RFC1122 compliant). Note
+    that TCP_KEEPIDLE and TCP_KEEPINTVL have to be set in seconds.
+
+  2007-03-08 Frédéric Bernon
+  * tcp.h: Keepalive values can be configured at compile time, but don't change
+    this unless you know what you're doing (default are RFC1122 compliant).
+
+  2007-03-08 Frédéric Bernon
+  * sockets.c, api.h, api_lib.c, tcpip.c, sys.h, sys.c, err.c, opt.h:
+    Implement LWIP_SO_RCVTIMEO configuration option to enable/disable SO_RCVTIMEO
+    on UDP sockets/netconn.
+
+  2007-03-08 Simon Goldschmidt
+  * snmp_msg.h, msg_in.c: SNMP UDP ports can be configured at compile time.
+
+  2007-03-06 Frédéric Bernon
+  * api.h, api_lib.c, sockets.h, sockets.c, tcpip.c, sys.h, sys.c, err.h: 
+    Implement SO_RCVTIMEO on UDP sockets/netconn.
+
+  2007-02-28 Kieran Mansley (based on patch from Simon Goldschmidt)
+  * api_lib.c, tcpip.c, memp.c, memp.h: make API msg structs allocated
+    on the stack and remove the API msg type from memp
+
+  2007-02-26 Jonathan Larmour (based on patch from Simon Goldschmidt)
+  * sockets.h, sockets.c: Move socket initialization to new
+    lwip_socket_init() function.
+    NOTE: this changes the API with ports. Ports will have to be
+    updated to call lwip_socket_init() now.
+
+  2007-02-26 Jonathan Larmour (based on patch from Simon Goldschmidt)
+  * api_lib.c: Use memcpy in netbuf_copy_partial.
+
+
+  ++ Bug fixes:
+
+  2008-03-17 Frédéric Bernon, Ed Kerekes
+  * igmp.h, igmp.c: Fix bug #22613 "IGMP iphdr problem" (could have
+    some problems to fill the IP header on some targets, use now the
+    ip.h macros to do it).
+
+  2008-03-13 Frédéric Bernon
+  * sockets.c: Fix bug #22435 "lwip_recvfrom with TCP break;". Using
+    (lwip_)recvfrom with valid "from" and "fromlen" parameters, on a
+    TCP connection caused a crash. Note that using (lwip_)recvfrom
+    like this is a bit slow and that using (lwip)getpeername is the
+    good lwip way to do it (so, using recv is faster on tcp sockets).
+
+  2008-03-12 Frédéric Bernon, Jonathan Larmour
+  * api_msg.c, contrib/apps/ping.c: Fix bug #22530 "api_msg.c's
+    recv_raw() does not consume data", and the ping sample (with
+    LWIP_SOCKET=1, the code did the wrong supposition that lwip_recvfrom
+    returned the IP payload, without the IP header).
+
+  2008-03-04 Jonathan Larmour
+  * mem.c, stats.c, mem.h: apply patch #6414 to avoid compiler errors
+  and/or warnings on some systems where mem_size_t and size_t differ.
+  * pbuf.c, ppp.c: Fix warnings on some systems with mem_malloc.
+
+  2008-03-04 Kieran Mansley (contributions by others) 
+  * Numerous small compiler error/warning fixes from contributions to
+    mailing list after 1.3.0 release candidate made.
+
+  2008-01-25 Cui hengbin (integrated by Frédéric Bernon)
+  * dns.c: Fix bug #22108 "DNS problem" caused by unaligned structures.
+
+  2008-01-15 Kieran Mansley
+  * tcp_out.c: BUG20511.  Modify persist timer to start when we are
+    prevented from sending by a small send window, not just a zero
+    send window.
+
+  2008-01-09 Jonathan Larmour
+  * opt.h, ip.c: Rename IP_OPTIONS define to IP_OPTIONS_ALLOWED to avoid
+    conflict with Linux system headers.
+
+  2008-01-06 Jonathan Larmour
+  * dhcp.c: fix bug #19927: "DHCP NACK problem" by clearing any existing set IP
+    address entirely on receiving a DHCPNAK, and restarting discovery.
+
+  2007-12-21 Simon Goldschmidt
+  * sys.h, api_lib.c, api_msg.c, sockets.c: fix bug #21698: "netconn->recv_avail
+    is not protected" by using new macros for interlocked access to modify/test
+    netconn->recv_avail.
+
+  2007-12-20 Kieran Mansley (based on patch from Oleg Tyshev)
+  * tcp_in.c: fix bug# 21535 (nrtx not reset correctly in SYN_SENT state)
+
+  2007-12-20 Kieran Mansley (based on patch from Per-Henrik Lundbolm)
+  * tcp.c, tcp_in.c, tcp_out.c, tcp.h: fix bug #20199 (better handling
+    of silly window avoidance and prevent lwIP from shrinking the window)
+
+  2007-12-04 Simon Goldschmidt
+  * tcp.c, tcp_in.c: fix bug #21699 (segment leak in ooseq processing when last
+    data packet was lost): add assert that all segment lists are empty in
+    tcp_pcb_remove before setting pcb to CLOSED state; don't directly set CLOSED
+    state from LAST_ACK in tcp_process
+
+  2007-12-02 Simon Goldschmidt
+  * sockets.h: fix bug #21654: exclude definition of struct timeval from #ifndef FD_SET
+    If including <sys/time.h> for system-struct timeval, LWIP_TIMEVAL_PRIVATE now
+    has to be set to 0 in lwipopts.h
+
+  2007-12-02 Simon Goldschmidt
+  * api_msg.c, api_lib.c: fix bug #21656 (recvmbox problem in netconn API): always
+    allocate a recvmbox in netconn_new_with_proto_and_callback. For a tcp-listen
+    netconn, this recvmbox is later freed and a new mbox is allocated for acceptmbox.
+    This is a fix for thread-safety and allocates all items needed for a netconn
+    when the netconn is created.
+
+  2007-11-30 Simon Goldschmidt
+  * udp.c: first attempt to fix bug #21655 (DHCP doesn't work reliably with multiple
+    netifs): if LWIP_DHCP is enabled, UDP packets to DHCP_CLIENT_PORT are passed
+    to netif->dhcp->pcb only (if that exists) and not to any other pcb for the same
+    port (only solution to let UDP pcbs 'bind' to a netif instead of an IP address)
+
+  2007-11-27 Simon Goldschmidt
+  * ip.c: fixed bug #21643 (udp_send/raw_send don't fail if netif is down) by
+    letting ip_route only use netifs that are up.
+
+  2007-11-27 Simon Goldschmidt
+  * err.h, api_lib.c, api_msg.c, sockets.c: Changed error handling: ERR_MEM, ERR_BUF
+    and ERR_RTE are seen as non-fatal, all other errors are fatal. netconns and
+    sockets block most operations once they have seen a fatal error.
+
+  2007-11-27 Simon Goldschmidt
+  * udp.h, udp.c, dhcp.c: Implemented new function udp_sendto_if which takes the
+    netif to send as an argument (to be able to send on netifs that are down).
+
+  2007-11-26 Simon Goldschmidt
+  * tcp_in.c: Fixed bug #21582: pcb->acked accounting can be wrong when ACKs
+    arrive out-of-order
+
+  2007-11-21 Simon Goldschmidt
+  * tcp.h, tcp_out.c, api_msg.c: Fixed bug #20287: tcp_output_nagle sends too early
+    Fixed the nagle algorithm; nagle now also works for all raw API applications
+    and has to be explicitly disabled with 'tcp_pcb->flags |= TF_NODELAY'
+
+  2007-11-12 Frédéric Bernon
+  * sockets.c, api.h, api_lib.c, api_msg.h, api_msg.c: Fixed bug #20900. Now, most
+    of the netconn_peer and netconn_addr processing is done inside tcpip_thread
+    context in do_getaddr.
+
+  2007-11-10 Simon Goldschmidt
+  * etharp.c: Fixed bug: assert fired when MEMP_ARP_QUEUE was empty (which can
+    happen any time). Now the packet simply isn't enqueued when out of memory.
+
+  2007-11-01 Simon Goldschmidt
+  * tcp.c, tcp_in.c: Fixed bug #21494: The send mss (pcb->mss) is set to 536 (or
+    TCP_MSS if that is smaller) as long as no MSS option is received from the
+    remote host.
+
+  2007-11-01 Simon Goldschmidt
+  * tcp.h, tcp.c, tcp_in.c: Fixed bug #21491: The MSS option sent (with SYN)
+    is now based on TCP_MSS instead of pcb->mss (on passive open now effectively
+    sending our configured TCP_MSS instead of the one received).
+
+  2007-11-01 Simon Goldschmidt
+  * tcp_in.c: Fixed bug #21181: On active open, the initial congestion window was
+    calculated based on the configured TCP_MSS, not on the MSS option received
+    with SYN+ACK.
+
+  2007-10-09 Simon Goldschmidt
+  * udp.c, inet.c, inet.h: Fixed UDPLite: send: Checksum was always generated too
+    short and also was generated wrong if checksum coverage != tot_len;
+    receive: checksum was calculated wrong if checksum coverage != tot_len
+
+  2007-10-08 Simon Goldschmidt
+  * mem.c: lfree was not updated in mem_realloc!
+
+  2007-10-07 Frédéric Bernon
+  * sockets.c, api.h, api_lib.c: First step to fix "bug #20900 : Potential
+    crash error problem with netconn_peer & netconn_addr". VERY IMPORTANT:
+    this change cause an API breakage for netconn_addr, since a parameter
+    type change. Any compiler should cause an error without any changes in
+    yours netconn_peer calls (so, it can't be a "silent change"). It also
+    reduce a little bit the footprint for socket layer (lwip_getpeername &
+    lwip_getsockname use now a common lwip_getaddrname function since 
+    netconn_peer & netconn_addr have the same parameters).
+
+  2007-09-20 Simon Goldschmidt
+  * tcp.c: Fixed bug #21080 (tcp_bind without check pcbs in TIME_WAIT state)
+    by checking  tcp_tw_pcbs also
+
+  2007-09-19 Simon Goldschmidt
+  * icmp.c: Fixed bug #21107 (didn't reset IP TTL in ICMP echo replies)
+
+  2007-09-15 Mike Kleshov
+  * mem.c: Fixed bug #21077 (inaccuracy in calculation of lwip_stat.mem.used)
+
+  2007-09-06 Frédéric Bernon
+  * several-files: replace some #include "arch/cc.h" by "lwip/arch.h", or simply remove
+    it as long as "lwip/opt.h" is included before (this one include "lwip/debug.h" which
+    already include "lwip/arch.h"). Like that, default defines are provided by "lwip/arch.h"
+    if they are not defined in cc.h, in the same spirit than "lwip/opt.h" for lwipopts.h.
+
+  2007-08-30 Frédéric Bernon
+  * igmp.h, igmp.c: Some changes to remove some redundant code, add some traces, 
+    and fix some coding style.
+
+  2007-08-28 Frédéric Bernon
+  * tcpip.c: Fix TCPIP_MSG_INPKT processing: now, tcpip_input can be used for any
+    kind of packets. These packets are considered like Ethernet packets (payload 
+    pointing to ethhdr) if the netif got the NETIF_FLAG_ETHARP flag. Else, packets 
+    are considered like IP packets (payload pointing to iphdr).
+
+  2007-08-27 Frédéric Bernon
+  * api.h, api_lib.c, api_msg.c: First fix for "bug #20900 : Potential crash error
+    problem with netconn_peer & netconn_addr". Introduce NETCONN_LISTEN netconn_state
+    and remove obsolete ones (NETCONN_RECV & NETCONN_ACCEPT).
+
+  2007-08-24 Kieran Mansley
+  * inet.c Modify (acc >> 16) test to ((acc >> 16) != 0) to help buggy
+    compiler (Paradigm C++)
+
+  2007-08-09 Frédéric Bernon, Bill Florac
+  * stats.h, stats.c, igmp.h, igmp.c, opt.h: Fix for bug #20503 : IGMP Improvement.
+    Introduce IGMP_STATS to centralize statistics management.
+
+  2007-08-09 Frédéric Bernon, Bill Florac
+  * udp.c: Fix for bug #20503 : IGMP Improvement. Enable to receive a multicast
+    packet on a udp pcb binded on an netif's IP address, and not on "any".
+
+  2007-08-09 Frédéric Bernon, Bill Florac
+  * igmp.h, igmp.c, ip.c: Fix minor changes from bug #20503 : IGMP Improvement.
+    This is mainly on using lookup/lookfor, and some coding styles...
+
+  2007-07-26 Frédéric Bernon (and "thedoctor")
+  * igmp.c: Fix bug #20595 to accept IGMPv3 "Query" messages.
+
+  2007-07-25 Simon Goldschmidt
+  * api_msg.c, tcp.c: Another fix for bug #20021: by not returning an error if
+    tcp_output fails in tcp_close, the code in do_close_internal gets simpler
+    (tcp_output is called again later from tcp timers).
+
+  2007-07-25 Simon Goldschmidt
+  * ip_frag.c: Fixed bug #20429: use the new pbuf_copy_partial instead of the old
+    copy_from_pbuf, which illegally modified the given pbuf.
+
+  2007-07-25 Simon Goldschmidt
+  * tcp_out.c: tcp_enqueue: pcb->snd_queuelen didn't work for chaine PBUF_RAMs:
+    changed snd_queuelen++ to snd_queuelen += pbuf_clen(p).
+
+  2007-07-24 Simon Goldschmidt
+  * api_msg.c, tcp.c: Fix bug #20480: Check the pcb passed to tcp_listen() for the
+    correct state (must be CLOSED).
+
+  2007-07-13 Thomas Taranowski (commited by Jared Grubb)
+  * memp.c: Fix bug #20478: memp_malloc returned NULL+MEMP_SIZE on failed
+    allocation. It now returns NULL.
+
+  2007-07-13 Frédéric Bernon
+  * api_msg.c: Fix bug #20318: api_msg "recv" callbacks don't call pbuf_free in
+    all error cases.
+
+  2007-07-13 Frédéric Bernon
+  * api_msg.c: Fix bug #20315: possible memory leak problem if tcp_listen failed,
+    because current code doesn't follow rawapi.txt documentation.
+
+  2007-07-13 Kieran Mansley
+  * src/core/tcp_in.c Apply patch#5741 from Oleg Tyshev to fix bug in
+    out of sequence processing of received packets
+
+  2007-07-03 Simon Goldschmidt
+  * nearly-all-files: Added assertions where PBUF_RAM pbufs are used and an
+    assumption is made that this pbuf is in one piece (i.e. not chained). These
+    assumptions clash with the possibility of converting to fully pool-based
+    pbuf implementations, where PBUF_RAM pbufs might be chained.
+
+  2007-07-03 Simon Goldschmidt
+  * api.h, api_lib.c, api_msg.c: Final fix for bug #20021 and some other problems
+    when closing tcp netconns: removed conn->sem, less context switches when
+    closing, both netconn_close and netconn_delete should safely close tcp
+    connections.
+
+  2007-07-02 Simon Goldschmidt
+  * ipv4/ip.h, ipv6/ip.h, opt.h, netif.h, etharp.h, ipv4/ip.c, netif.c, raw.c,
+    tcp_out.c, udp.c, etharp.c: Added option LWIP_NETIF_HWADDRHINT (default=off)
+    to cache ARP table indices with each pcb instead of single-entry cache for
+    the complete stack.
+
+  2007-07-02 Simon Goldschmidt
+  * tcp.h, tcp.c, tcp_in.c, tcp_out.c: Added some ASSERTS and casts to prevent
+    warnings when assigning to smaller types.
+
+  2007-06-28 Simon Goldschmidt
+  * tcp_out.c: Added check to prevent tcp_pcb->snd_queuelen from overflowing.
+
+  2007-06-28 Simon Goldschmidt
+  * tcp.h: Fixed bug #20287: Fixed nagle algorithm (sending was done too early if
+    a segment contained chained pbufs)
+
+  2007-06-28 Frédéric Bernon
+  * autoip.c: replace most of rand() calls by a macro LWIP_AUTOIP_RAND which compute
+    a "pseudo-random" value based on netif's MAC and some autoip fields. It's always
+    possible to define this macro in your own lwipopts.h to always use C library's
+    rand(). Note that autoip_create_rand_addr doesn't use this macro.
+
+  2007-06-28 Frédéric Bernon
+  * netifapi.h, netifapi.c, tcpip.h, tcpip.c: Update code to handle the option
+    LWIP_TCPIP_CORE_LOCKING, and do some changes to be coherent with last modifications
+    in api_lib/api_msg (use pointers and not type with table, etc...) 
+
+  2007-06-26 Simon Goldschmidt
+  * udp.h: Fixed bug #20259: struct udp_hdr was lacking the packin defines.
+
+  2007-06-25 Simon Goldschmidt
+  * udp.c: Fixed bug #20253: icmp_dest_unreach was called with a wrong p->payload
+    for udp packets with no matching pcb.
+
+  2007-06-25 Simon Goldschmidt
+  * udp.c: Fixed bug #20220: UDP PCB search in udp_input(): a non-local match
+    could get udp input packets if the remote side matched.
+
+  2007-06-13 Simon Goldschmidt
+  * netif.c: Fixed bug #20180 (TCP pcbs listening on IP_ADDR_ANY could get
+    changed in netif_set_ipaddr if previous netif->ip_addr.addr was 0.
+
+  2007-06-13 Simon Goldschmidt
+  * api_msg.c: pcb_new sets conn->err if protocol is not implemented
+    -> netconn_new_..() does not allocate a new connection for unsupported
+    protocols.
+
+  2007-06-13 Frédéric Bernon, Simon Goldschmidt
+  * api_lib.c: change return expression in netconn_addr and netconn_peer, because
+    conn->err was reset to ERR_OK without any reasons (and error was lost)...
+
+  2007-06-13 Frédéric Bernon, Matthias Weisser
+  * opt.h, mem.h, mem.c, memp.c, pbuf.c, ip_frag.c, vj.c: Fix bug #20162. Rename
+    MEM_ALIGN in LWIP_MEM_ALIGN and MEM_ALIGN_SIZE in LWIP_MEM_ALIGN_SIZE to avoid
+    some macro names collision with some OS macros.
+
+  2007-06-11 Simon Goldschmidt
+  * udp.c: UDP Lite: corrected the use of chksum_len (based on RFC3828: if it's 0,
+    create checksum over the complete packet. On RX, if it's < 8 (and not 0),
+    discard the packet. Also removed the duplicate 'udphdr->chksum = 0' for both
+    UDP & UDP Lite.
+
+  2007-06-11 Srinivas Gollakota & Oleg Tyshev
+  * tcp_out.c: Fix for bug #20075 : "A problem with keep-alive timer and TCP flags"
+    where TCP flags wasn't initialized in tcp_keepalive.
+
+  2007-06-03 Simon Goldschmidt
+  * udp.c: udp_input(): Input pbuf was not freed if pcb had no recv function
+    registered, p->payload was modified without modifying p->len if sending
+    icmp_dest_unreach() (had no negative effect but was definitively wrong).
+
+  2007-06-03 Simon Goldschmidt
+  * icmp.c: Corrected bug #19937: For responding to an icmp echo request, icmp
+    re-used the input pbuf even if that didn't have enough space to include the
+    link headers. Now the space is tested and a new pbuf is allocated for the
+    echo response packet if the echo request pbuf isn't big enough.
+
+  2007-06-01 Simon Goldschmidt
+  * sockets.c: Checked in patch #5914: Moved sockopt processing into tcpip_thread.
+
+  2007-05-23 Frédéric Bernon
+  * api_lib.c, sockets.c: Fixed bug #5958 for netconn_listen (acceptmbox only
+    allocated by do_listen if success) and netconn_accept errors handling. In
+    most of api_lib functions, we replace some errors checkings like "if (conn==NULL)"
+    by ASSERT, except for netconn_delete.
+
+  2007-05-23 Frédéric Bernon
+  * api_lib.c: Fixed bug #5957 "Safe-thread problem inside netconn_recv" to return
+    an error code if it's impossible to fetch a pbuf on a TCP connection (and not
+    directly close the recvmbox).
+
+  2007-05-22 Simon Goldschmidt
+  * tcp.c: Fixed bug #1895 (tcp_bind not correct) by introducing a list of
+    bound but unconnected (and non-listening) tcp_pcbs.
+
+  2007-05-22 Frédéric Bernon
+  * sys.h, sys.c, api_lib.c, tcpip.c: remove sys_mbox_fetch_timeout() (was only
+    used for LWIP_SO_RCVTIMEO option) and use sys_arch_mbox_fetch() instead of
+    sys_mbox_fetch() in api files. Now, users SHOULD NOT use internal lwIP features
+    like "sys_timeout" in their application threads.
+
+  2007-05-22 Frédéric Bernon
+  * api.h, api_lib.c, api_msg.h, api_msg.c: change the struct api_msg_msg to see
+    which parameters are used by which do_xxx function, and to avoid "misusing"
+    parameters (patch #5938).
+
+  2007-05-22 Simon Goldschmidt
+  * api_lib.c, api_msg.c, raw.c, api.h, api_msg.h, raw.h: Included patch #5938:
+    changed raw_pcb.protocol from u16_t to u8_t since for IPv4 and IPv6, proto
+    is only 8 bits wide. This affects the api, as there, the protocol was
+    u16_t, too.
+
+  2007-05-18 Simon Goldschmidt
+  * memp.c: addition to patch #5913: smaller pointer was returned but
+    memp_memory was the same size -> did not save memory.
+
+  2007-05-16 Simon Goldschmidt
+  * loopif.c, slipif.c: Fix bug #19729: free pbuf if netif->input() returns
+    != ERR_OK.
+
+  2007-05-16 Simon Goldschmidt
+  * api_msg.c, udp.c: If a udp_pcb has a local_ip set, check if it is the same
+    as the one of the netif used for sending to prevent sending from old
+    addresses after a netif address gets changed (partly fixes bug #3168).
+
+  2007-05-16 Frédéric Bernon
+  * tcpip.c, igmp.h, igmp.c: Fixed bug "#19800 : IGMP: igmp_tick() will not work
+    with NO_SYS=1". Note that igmp_init is always in tcpip_thread (and not in 
+    tcpip_init) because we have to be sure that network interfaces are already
+    added (mac filter is updated only in igmp_init for the moment).
+
+  2007-05-16 Simon Goldschmidt
+  * mem.c, memp.c: Removed semaphores from memp, changed sys_sem_wait calls
+    into sys_arch_sem_wait calls to prevent timers from running while waiting
+    for the heap. This fixes bug #19167.
+
+  2007-05-13 Simon Goldschmidt
+  * tcp.h, sockets.h, sockets.c: Fixed bug from patch #5865 by moving the defines
+    for socket options (lwip_set/-getsockopt) used with level IPPROTO_TCP from
+    tcp.h to sockets.h.
+
+  2007-05-07 Simon Goldschmidt
+  * mem.c: Another attempt to fix bug #17922.
+
+  2007-05-04 Simon Goldschmidt
+  * pbuf.c, pbuf.h, etharp.c: Further update to ARP queueing: Changed pbuf_copy()
+    implementation so that it can be reused (don't allocate the target
+    pbuf inside pbuf_copy()).
+
+  2007-05-04 Simon Goldschmidt
+  * memp.c: checked in patch #5913: in memp_malloc() we can return memp as mem
+    to save a little RAM (next pointer of memp is not used while not in pool).
+
+  2007-05-03 "maq"
+  * sockets.c: Fix ioctl FIONREAD when some data remains from last recv.
+    (patch #3574).
+
+  2007-04-23 Simon Goldschmidt
+  * loopif.c, loopif.h, opt.h, src/netif/FILES: fix bug #2595: "loopif results
+    in NULL reference for incoming TCP packets". Loopif has to be configured
+    (using LWIP_LOOPIF_MULTITHREADING) to directly call netif->input()
+    (multithreading environments, e.g. netif->input() = tcpip_input()) or
+    putting packets on a list that is fed to the stack by calling loopif_poll()
+    (single-thread / NO_SYS / polling environment where e.g.
+    netif->input() = ip_input).
+
+  2007-04-17 Jonathan Larmour
+  * pbuf.c: Use s32_t in pbuf_realloc(), as an s16_t can't reliably hold
+    the difference between two u16_t's.
+  * sockets.h: FD_SETSIZE needs to match number of sockets, which is
+    MEMP_NUM_NETCONN in sockets.c right now.
+
+  2007-04-12 Jonathan Larmour
+  * icmp.c: Reset IP header TTL in ICMP ECHO responses (bug #19580).
+
+  2007-04-12 Kieran Mansley
+  * tcp.c, tcp_in.c, tcp_out.c, tcp.h: Modify way the retransmission
+    timer is reset to fix bug#19434, with help from Oleg Tyshev.
+
+  2007-04-11 Simon Goldschmidt
+  * etharp.c, pbuf.c, pbuf.h: 3rd fix for bug #11400 (arp-queuing): More pbufs than
+    previously thought need to be copied (everything but PBUF_ROM!). Cleaned up
+    pbuf.c: removed functions no needed any more (by etharp).
+
+  2007-04-11 Kieran Mansley
+  * inet.c, ip_addr.h, sockets.h, sys.h, tcp.h: Apply patch #5745: Fix
+    "Constant is long" warnings with 16bit compilers.  Contributed by
+    avatar@mmlab.cse.yzu.edu.tw
+
+  2007-04-05 Frédéric Bernon, Jonathan Larmour
+  * api_msg.c: Fix bug #16830: "err_tcp() posts to connection mailbox when no pend on
+    the mailbox is active". Now, the post is only done during a connect, and do_send,
+    do_write and do_join_leave_group don't do anything if a previous error was signaled.
+
+  2007-04-03 Frédéric Bernon
+  * ip.c: Don't set the IP_DF ("Don't fragment") flag in the IP header in IP output
+    packets. See patch #5834.
+
+  2007-03-30 Frédéric Bernon
+  * api_msg.c: add a "pcb_new" helper function to avoid redundant code, and to add
+    missing  pcb allocations checking (in do_bind, and for each raw_new). Fix style.
+
+  2007-03-30 Frédéric Bernon
+  * most of files: prefix all debug.h define with "LWIP_" to avoid any conflict with
+    others environment defines (these were too "generic").
+
+  2007-03-28 Frédéric Bernon
+  * api.h, api_lib.c, sockets.c: netbuf_ref doesn't check its internal pbuf_alloc call
+    result and can cause a crash. lwip_send now check netbuf_ref result.
+
+  2007-03-28 Simon Goldschmidt
+  * sockets.c Remove "#include <errno.h>" from sockets.c to avoid multiple
+    definition of macros (in errno.h and lwip/arch.h) if LWIP_PROVIDE_ERRNO is
+    defined. This is the way it should have been already (looking at
+    doc/sys_arch.txt)
+
+  2007-03-28 Kieran Mansley
+  * opt.h Change default PBUF_POOL_BUFSIZE (again) to accomodate default MSS +
+    IP and TCP headers *and* physical link headers
+
+  2007-03-26 Frédéric Bernon (based on patch from Dmitry Potapov)
+  * api_lib.c: patch for netconn_write(), fixes a possible race condition which cause
+    to send some garbage. It is not a definitive solution, but the patch does solve
+    the problem for most cases.
+
+  2007-03-22 Frédéric Bernon
+  * api_msg.h, api_msg.c: Remove obsolete API_MSG_ACCEPT and do_accept (never used).
+
+  2007-03-22 Frédéric Bernon
+  * api_lib.c: somes resources couldn't be freed if there was errors during
+    netconn_new_with_proto_and_callback.
+
+  2007-03-22 Frédéric Bernon
+  * ethernetif.c: update netif->input calls to check return value. In older ports,
+    it's a good idea to upgrade them, even if before, there could be another problem
+    (access to an uninitialized mailbox).
+
+  2007-03-21 Simon Goldschmidt
+  * sockets.c: fixed bug #5067 (essentialy a signed/unsigned warning fixed
+    by casting to unsigned).
+
+  2007-03-21 Frédéric Bernon
+  * api_lib.c, api_msg.c, tcpip.c: integrate sys_mbox_fetch(conn->mbox, NULL) calls from
+    api_lib.c to tcpip.c's tcpip_apimsg(). Now, use a local variable and not a
+    dynamic one from memp to send tcpip_msg to tcpip_thread in a synchrone call.
+    Free tcpip_msg from tcpip_apimsg is not done in tcpip_thread. This give a
+    faster and more reliable communication between api_lib and tcpip.
+
+  2007-03-21 Frédéric Bernon
+  * opt.h: Add LWIP_NETIF_CALLBACK (to avoid compiler warning) and set it to 0.
+
+  2007-03-21 Frédéric Bernon
+  * api_msg.c, igmp.c, igmp.h: Fix C++ style comments
+
+  2007-03-21 Kieran Mansley
+  * opt.h Change default PBUF_POOL_BUFSIZE to accomodate default MSS +
+    IP and TCP headers
+
+  2007-03-21 Kieran Mansley
+  * Fix all uses of pbuf_header to check the return value.  In some
+    cases just assert if it fails as I'm not sure how to fix them, but
+    this is no worse than before when they would carry on regardless
+    of the failure.
+
+  2007-03-21 Kieran Mansley
+  * sockets.c, igmp.c, igmp.h, memp.h: Fix C++ style comments and
+    comment out missing header include in icmp.c
+
+  2007-03-20 Frédéric Bernon
+  * memp.h, stats.c: Fix stats_display function where memp_names table wasn't
+    synchronized with memp.h.
+
+  2007-03-20 Frédéric Bernon
+  * tcpip.c: Initialize tcpip's mbox, and verify if initialized in tcpip_input,
+    tcpip_ethinput, tcpip_callback, tcpip_apimsg, to fix a init problem with 
+    network interfaces. Also fix a compiler warning.
+
+  2007-03-20 Kieran Mansley
+  * udp.c: Only try and use pbuf_header() to make space for headers if
+    not a ROM or REF pbuf.
+
+  2007-03-19 Frédéric Bernon
+  * api_msg.h, api_msg.c, tcpip.h, tcpip.c: Add return types to tcpip_apimsg()
+    and api_msg_post().
+
+  2007-03-19 Frédéric Bernon
+  * Remove unimplemented "memp_realloc" function from memp.h.
+
+  2007-03-11 Simon Goldschmidt
+  * pbuf.c: checked in patch #5796: pbuf_alloc: len field claculation caused
+    memory corruption.
+
+  2007-03-11 Simon Goldschmidt (based on patch from Dmitry Potapov)
+  * api_lib.c, sockets.c, api.h, api_msg.h, sockets.h: Fixed bug #19251
+    (missing `const' qualifier in socket functions), to get more compatible to
+    standard POSIX sockets.
+
+  2007-03-11 Frédéric Bernon (based on patch from Dmitry Potapov)
+  * sockets.c: Add asserts inside bind, connect and sendto to check input
+    parameters. Remove excessive set_errno() calls after get_socket(), because
+    errno is set inside of get_socket(). Move last sock_set_errno() inside
+    lwip_close.
+
+  2007-03-09 Simon Goldschmidt
+  * memp.c: Fixed bug #11400: New etharp queueing introduced bug: memp_memory
+    was allocated too small.
+
+  2007-03-06 Simon Goldschmidt
+  * tcpip.c: Initialize dhcp timers in tcpip_thread (if LWIP_DHCP) to protect
+    the stack from concurrent access.
+
+  2007-03-06 Frédéric Bernon, Dmitry Potapov
+  * tcpip.c, ip_frag.c, ethernetif.c: Fix some build problems, and a redundancy
+    call to "lwip_stats.link.recv++;" in low_level_input() & ethernetif_input().
+
+  2007-03-06 Simon Goldschmidt
+  * ip_frag.c, ip_frag.h: Reduce code size: don't include code in those files
+    if IP_FRAG == 0 and IP_REASSEMBLY == 0
+
+  2007-03-06 Frédéric Bernon, Simon Goldschmidt
+  * opt.h, ip_frag.h, tcpip.h, tcpip.c, ethernetif.c: add new configuration
+    option named ETHARP_TCPIP_ETHINPUT, which enable the new tcpip_ethinput.
+    Allow to do ARP processing for incoming packets inside tcpip_thread
+    (protecting ARP layer against concurrent access). You can also disable
+    old code using tcp_input with new define ETHARP_TCPIP_INPUT set to 0.
+    Older ports have to use tcpip_ethinput.
+
+  2007-03-06 Simon Goldschmidt (based on patch from Dmitry Potapov)
+  * err.h, err.c: fixed compiler warning "initialization dircards qualifiers
+    from pointer target type"
+
+  2007-03-05 Frédéric Bernon
+  * opt.h, sockets.h: add new configuration options (LWIP_POSIX_SOCKETS_IO_NAMES,
+    ETHARP_TRUST_IP_MAC, review SO_REUSE)
+
+  2007-03-04 Frédéric Bernon
+  * api_msg.c: Remove some compiler warnings : parameter "pcb" was never
+    referenced.
+
+  2007-03-04 Frédéric Bernon
+  * api_lib.c: Fix "[patch #5764] api_lib.c cleanup: after patch #5687" (from
+    Dmitry Potapov).
+    The api_msg struct stay on the stack (not moved to netconn struct).
+
+  2007-03-04 Simon Goldschmidt (based on patch from Dmitry Potapov)
+  * pbuf.c: Fix BUG#19168 - pbuf_free can cause deadlock (if
+    SYS_LIGHTWEIGHT_PROT=1 & freeing PBUF_RAM when mem_sem is not available)
+    Also fixed cast warning in pbuf_alloc()
+
+  2007-03-04 Simon Goldschmidt
+  * etharp.c, etharp.h, memp.c, memp.h, opt.h: Fix BUG#11400 - don't corrupt
+    existing pbuf chain when enqueuing multiple pbufs to a pending ARP request
+
+  2007-03-03 Frédéric Bernon
+  * udp.c: remove obsolete line "static struct udp_pcb *pcb_cache = NULL;"
+    It is static, and never used in udp.c except udp_init().
+
+  2007-03-02 Simon Goldschmidt
+  * tcpip.c: Moved call to ip_init(), udp_init() and tcp_init() from
+    tcpip_thread() to tcpip_init(). This way, raw API connections can be
+    initialized before tcpip_thread is running (e.g. before OS is started)
+
+  2007-03-02 Frédéric Bernon
+  * rawapi.txt: Fix documentation mismatch with etharp.h about etharp_tmr's call
+    interval.
+
+  2007-02-28 Kieran Mansley 
+  * pbuf.c: Fix BUG#17645 - ensure pbuf payload pointer is not moved
+    outside the region of the pbuf by pbuf_header()
+
+  2007-02-28 Kieran Mansley 
+  * sockets.c: Fix BUG#19161 - ensure milliseconds timeout is non-zero
+    when supplied timeout is also non-zero 
+
+(STABLE-1.2.0)
+
+  2006-12-05 Leon Woestenberg
+  * CHANGELOG: Mention STABLE-1.2.0 release.
+
+  ++ New features:
+
+  2006-12-01 Christiaan Simons
+  * mem.h, opt.h: Added MEM_LIBC_MALLOC option.
+    Note this is a workaround. Currently I have no other options left.
+
+  2006-10-26 Christiaan Simons (accepted patch by Jonathan Larmour)
+  * ipv4/ip_frag.c: rename MAX_MTU to IP_FRAG_MAX_MTU and move define
+    to include/lwip/opt.h.
+  * ipv4/lwip/ip_frag.h: Remove unused IP_REASS_INTERVAL.
+    Move IP_REASS_MAXAGE and IP_REASS_BUFSIZE to include/lwip/opt.h.
+  * opt.h: Add above new options.
+
+  2006-08-18 Christiaan Simons
+  * tcp_{in,out}.c: added SNMP counters.
+  * ipv4/ip.c: added SNMP counters.
+  * ipv4/ip_frag.c: added SNMP counters.
+
+  2006-08-08 Christiaan Simons
+  * etharp.{c,h}: added etharp_find_addr() to read
+    (stable) ethernet/IP address pair from ARP table
+
+  2006-07-14 Christiaan Simons
+  * mib_structs.c: added
+  * include/lwip/snmp_structs.h: added
+  * netif.{c,h}, netif/ethernetif.c: added SNMP statistics to netif struct
+
+  2006-07-06 Christiaan Simons
+  * snmp/asn1_{enc,dec}.c added
+  * snmp/mib2.c added
+  * snmp/msg_{in,out}.c added
+  * include/lwip/snmp_asn1.h added
+  * include/lwip/snmp_msg.h added
+  * doc/snmp_agent.txt added
+
+  2006-03-29 Christiaan Simons
+  * inet.c, inet.h: Added platform byteswap support.
+    Added LWIP_PLATFORM_BYTESWAP define (defaults to 0) and
+    optional LWIP_PLATFORM_HTONS(), LWIP_PLATFORM_HTONL() macros.
+
+  ++ Bug fixes:
+
+  2006-11-30 Christiaan Simons
+  * dhcp.c: Fixed false triggers of request_timeout.
+
+  2006-11-28 Christiaan Simons
+  * netif.c: In netif_add() fixed missing clear of ip_addr, netmask, gw and flags.
+
+  2006-10-11 Christiaan Simons
+  * api_lib.c etharp.c, ip.c, memp.c, stats.c, sys.{c,h} tcp.h:
+    Partially accepted patch #5449 for ANSI C compatibility / build fixes.
+  * ipv4/lwip/ip.h ipv6/lwip/ip.h: Corrected UDP-Lite protocol
+    identifier from 170 to 136 (bug #17574).
+
+  2006-10-10 Christiaan Simons
+  * api_msg.c: Fixed Nagle algorithm as reported by Bob Grice.
+
+  2006-08-17 Christiaan Simons
+  * udp.c: Fixed bug #17200, added check for broadcast
+    destinations for PCBs bound to a unicast address.
+
+  2006-08-07 Christiaan Simons
+  * api_msg.c: Flushing TCP output in do_close() (bug #15926).
+
+  2006-06-27 Christiaan Simons
+  * api_msg.c: Applied patch for cold case (bug #11135).
+    In accept_function() ensure newconn->callback is always initialized.
+
+  2006-06-15 Christiaan Simons
+  * mem.h: added MEM_SIZE_F alias to fix an ancient cold case (bug #1748),
+    facilitate printing of mem_size_t and u16_t statistics.
+
+  2006-06-14 Christiaan Simons
+  * api_msg.c: Applied patch #5146 to handle allocation failures
+    in accept() by Kevin Lawson.
+
+  2006-05-26 Christiaan Simons
+  * api_lib.c: Removed conn->sem creation and destruction 
+    from netconn_write() and added sys_sem_new to netconn_new_*.
+
+(STABLE-1_1_1)
+
+  2006-03-03  Christiaan Simons
+  * ipv4/ip_frag.c: Added bound-checking assertions on ip_reassbitmap
+    access and added pbuf_alloc() return value checks.
+
+  2006-01-01  Leon Woestenberg <leon.woestenberg@gmx.net>
+  * tcp_{in,out}.c, tcp_out.c: Removed 'even sndbuf' fix in TCP, which is
+    now handled by the checksum routine properly.
+
+  2006-02-27  Leon Woestenberg <leon.woestenberg@gmx.net>
+   * pbuf.c: Fix alignment; pbuf_init() would not work unless
+     pbuf_pool_memory[] was properly aligned. (Patch by Curt McDowell.)
+
+  2005-12-20  Leon Woestenberg <leon.woestenberg@gmx.net>
+  * tcp.c: Remove PCBs which stay in LAST_ACK state too long. Patch
+    submitted by Mitrani Hiroshi.
+
+  2005-12-15  Christiaan Simons
+  * inet.c: Disabled the added summing routine to preserve code space.
+
+  2005-12-14  Leon Woestenberg <leon.woestenberg@gmx.net>
+  * tcp_in.c: Duplicate FIN ACK race condition fix by Kelvin Lawson.
+    Added Curt McDowell's optimized checksumming routine for future
+    inclusion. Need to create test case for unaliged, aligned, odd,
+    even length combination of cases on various endianess machines.
+
+  2005-12-09  Christiaan Simons
+  * inet.c: Rewrote standard checksum routine in proper portable C.
+
+  2005-11-25  Christiaan Simons
+  * udp.c tcp.c: Removed SO_REUSE hack. Should reside in socket code only.
+  * *.c: introduced cc.h LWIP_DEBUG formatters matching the u16_t, s16_t,
+    u32_t, s32_t typedefs. This solves most debug word-length assumes.
+
+  2005-07-17 Leon Woestenberg <leon.woestenberg@gmx.net>
+  * inet.c: Fixed unaligned 16-bit access in the standard checksum
+    routine by Peter Jolasson.
+  * slipif.c: Fixed implementation assumption of single-pbuf datagrams.
+
+  2005-02-04 Leon Woestenberg <leon.woestenberg@gmx.net>
+  * tcp_out.c: Fixed uninitialized 'queue' referenced in memerr branch.
+  * tcp_{out|in}.c: Applied patch fixing unaligned access.
+
+  2005-01-04 Leon Woestenberg <leon.woestenberg@gmx.net>
+  * pbuf.c: Fixed missing semicolon after LWIP_DEBUG statement.
+
+  2005-01-03 Leon Woestenberg <leon.woestenberg@gmx.net>
+  * udp.c: UDP pcb->recv() was called even when it was NULL.
+
+(STABLE-1_1_0)
+
+  2004-12-28 Leon Woestenberg <leon.woestenberg@gmx.net>
+  * etharp.*: Disabled multiple packets on the ARP queue.
+    This clashes with TCP queueing.
+
+  2004-11-28 Leon Woestenberg <leon.woestenberg@gmx.net>
+  * etharp.*: Fixed race condition from ARP request to ARP timeout.
+    Halved the ARP period, doubled the period counts.
+    ETHARP_MAX_PENDING now should be at least 2. This prevents
+    the counter from reaching 0 right away (which would allow
+    too little time for ARP responses to be received).
+
+  2004-11-25 Leon Woestenberg <leon.woestenberg@gmx.net>
+  * dhcp.c: Decline messages were not multicast but unicast.
+  * etharp.c: ETHARP_CREATE is renamed to ETHARP_TRY_HARD.
+    Do not try hard to insert arbitrary packet's source address,
+    etharp_ip_input() now calls etharp_update() without ETHARP_TRY_HARD. 
+    etharp_query() now always DOES call ETHARP_TRY_HARD so that users
+    querying an address will see it appear in the cache (DHCP could
+    suffer from this when a server invalidly gave an in-use address.)
+  * ipv4/ip_addr.h: Renamed ip_addr_maskcmp() to _netcmp() as we are
+    comparing network addresses (identifiers), not the network masks
+    themselves.
+  * ipv4/ip_addr.c: ip_addr_isbroadcast() now checks that the given
+    IP address actually belongs to the network of the given interface.
+
+  2004-11-24 Kieran Mansley <kjm25@cam.ac.uk>
+  * tcp.c: Increment pcb->snd_buf when ACK is received in SYN_SENT state.
+
+(STABLE-1_1_0-RC1)
+
+  2004-10-16 Kieran Mansley <kjm25@cam.ac.uk>
+  * tcp.c: Add code to tcp_recved() to send an ACK (window update) immediately,
+    even if one is already pending, if the rcv_wnd is above a threshold
+    (currently TCP_WND/2). This avoids waiting for a timer to expire to send a
+    delayed ACK in order to open the window if the stack is only receiving data.
+
+  2004-09-12 Kieran Mansley <kjm25@cam.ac.uk>
+  * tcp*.*: Retransmit time-out handling improvement by Sam Jansen.
+
+  2004-08-20 Tony Mountifield <tony@softins.co.uk>
+  * etharp.c: Make sure the first pbuf queued on an ARP entry
+    is properly ref counted.
+
+  2004-07-27 Tony Mountifield <tony@softins.co.uk>
+  * debug.h: Added (int) cast in LWIP_DEBUGF() to avoid compiler
+    warnings about comparison.
+  * pbuf.c: Stopped compiler complaining of empty if statement
+    when LWIP_DEBUGF() empty.  Closed an unclosed comment.
+  * tcp.c: Stopped compiler complaining of empty if statement
+    when LWIP_DEBUGF() empty.
+  * ip.h Corrected IPH_TOS() macro: returns a byte, so doesn't need htons().
+  * inet.c: Added a couple of casts to quiet the compiler.
+    No need to test isascii(c) before isdigit(c) or isxdigit(c).
+
+  2004-07-22 Tony Mountifield <tony@softins.co.uk>
+  * inet.c: Made data types consistent in inet_ntoa().
+    Added casts for return values of checksum routines, to pacify compiler.
+  * ip_frag.c, tcp_out.c, sockets.c, pbuf.c
+    Small corrections to some debugging statements, to pacify compiler.
+
+  2004-07-21 Tony Mountifield <tony@softins.co.uk>
+  * etharp.c: Removed spurious semicolon and added missing end-of-comment.
+  * ethernetif.c Updated low_level_output() to match prototype for
+    netif->linkoutput and changed low_level_input() similarly for consistency.
+  * api_msg.c: Changed recv_raw() from int to u8_t, to match prototype
+    of raw_recv() in raw.h and so avoid compiler error.
+  * sockets.c: Added trivial (int) cast to keep compiler happier.
+  * ip.c, netif.c Changed debug statements to use the tidier ip4_addrN() macros.
+
+(STABLE-1_0_0)
+
+  ++ Changes:
+
+  2004-07-05 Leon Woestenberg <leon.woestenberg@gmx.net>
+  * sockets.*: Restructured LWIP_PRIVATE_TIMEVAL. Make sure
+    your cc.h file defines this either 1 or 0. If non-defined,
+    defaults to 1.
+  * .c: Added <string.h> and <errno.h> includes where used.
+  * etharp.c: Made some array indices unsigned.
+
+  2004-06-27 Leon Woestenberg <leon.woestenberg@gmx.net>
+  * netif.*: Added netif_set_up()/down().
+  * dhcp.c: Changes to restart program flow.
+
+  2004-05-07 Leon Woestenberg <leon.woestenberg@gmx.net>
+  * etharp.c: In find_entry(), instead of a list traversal per candidate, do a
+    single-pass lookup for different candidates. Should exploit locality.
+
+  2004-04-29 Leon Woestenberg <leon.woestenberg@gmx.net>
+  * tcp*.c: Cleaned up source comment documentation for Doxygen processing.
+  * opt.h: ETHARP_ALWAYS_INSERT option removed to comply with ARP RFC.
+  * etharp.c: update_arp_entry() only adds new ARP entries when adviced to by
+    the caller. This deprecates the ETHARP_ALWAYS_INSERT overrule option.
+
+  ++ Bug fixes:
+
+  2004-04-27 Leon Woestenberg <leon.woestenberg@gmx.net>
+  * etharp.c: Applied patch of bug #8708 by Toni Mountifield with a solution
+    suggested by Timmy Brolin. Fix for 32-bit processors that cannot access
+    non-aligned 32-bit words, such as soms 32-bit TCP/IP header fields. Fix
+    is to prefix the 14-bit Ethernet headers with two padding bytes.
+
+  2004-04-23 Leon Woestenberg <leon.woestenberg@gmx.net>
+  * ip_addr.c: Fix in the ip_addr_isbroadcast() check.
+  * etharp.c: Fixed the case where the packet that initiates the ARP request
+    is not queued, and gets lost. Fixed the case where the packets destination
+    address is already known; we now always queue the packet and perform an ARP
+    request.
+
+(STABLE-0_7_0)
+
+  ++ Bug fixes:
+
+  * Fixed TCP bug for SYN_SENT to ESTABLISHED state transition.
+  * Fixed TCP bug in dequeueing of FIN from out of order segment queue.
+  * Fixed two possible NULL references in rare cases.
+
+(STABLE-0_6_6)
+
+  ++ Bug fixes:
+
+  * Fixed DHCP which did not include the IP address in DECLINE messages.
+
+  ++ Changes:
+
+  * etharp.c has been hauled over a bit.
+
+(STABLE-0_6_5)
+
+  ++ Bug fixes:
+
+  * Fixed TCP bug induced by bad window resizing with unidirectional TCP traffic.
+  * Packets sent from ARP queue had invalid source hardware address.
+
+  ++ Changes:
+
+  * Pass-by ARP requests do now update the cache.
+
+  ++ New features:
+
+  * No longer dependent on ctype.h.
+  * New socket options.
+  * Raw IP pcb support.
+
+(STABLE-0_6_4)
+
+  ++ Bug fixes:
+
+  * Some debug formatters and casts fixed.
+  * Numereous fixes in PPP.
+
+  ++ Changes:
+
+  * DEBUGF now is LWIP_DEBUGF
+  * pbuf_dechain() has been re-enabled.
+  * Mentioned the changed use of CVS branches in README.
+
+(STABLE-0_6_3)
+
+  ++ Bug fixes:
+
+  * Fixed pool pbuf memory leak in pbuf_alloc().
+    Occured if not enough PBUF_POOL pbufs for a packet pbuf chain.
+    Reported by Savin Zlobec.
+
+  * PBUF_POOL chains had their tot_len field not set for non-first
+    pbufs. Fixed in pbuf_alloc().
+
+  ++ New features:
+
+  * Added PPP stack contributed by Marc Boucher
+
+  ++ Changes:
+
+  * Now drops short packets for ICMP/UDP/TCP protocols. More robust.
+
+  * ARP queueuing now queues the latest packet instead of the first.
+    This is the RFC recommended behaviour, but can be overridden in
+    lwipopts.h.
+
+(0.6.2)
+
+  ++ Bugfixes:
+
+  * TCP has been fixed to deal with the new use of the pbuf->ref
+    counter.
+
+  * DHCP dhcp_inform() crash bug fixed.
+
+  ++ Changes:
+
+  * Removed pbuf_pool_free_cache and pbuf_pool_alloc_cache. Also removed
+    pbuf_refresh(). This has sped up pbuf pool operations considerably.
+    Implemented by David Haas.
+
+(0.6.1)
+
+  ++ New features:
+
+  * The packet buffer implementation has been enhanced to support
+    zero-copy and copy-on-demand for packet buffers which have their
+    payloads in application-managed memory.
+    Implemented by David Haas.
+
+    Use PBUF_REF to make a pbuf refer to RAM. lwIP will use zero-copy
+    if an outgoing packet can be directly sent on the link, or perform
+    a copy-on-demand when necessary.
+
+    The application can safely assume the packet is sent, and the RAM
+    is available to the application directly after calling udp_send()
+    or similar function.
+
+  ++ Bugfixes:
+
+  * ARP_QUEUEING should now correctly work for all cases, including
+    PBUF_REF.
+    Implemented by Leon Woestenberg.
+
+  ++ Changes:
+
+  * IP_ADDR_ANY is no longer a NULL pointer. Instead, it is a pointer
+    to a '0.0.0.0' IP address.
+
+  * The packet buffer implementation is changed. The pbuf->ref counter
+    meaning has changed, and several pbuf functions have been
+    adapted accordingly.
+
+  * netif drivers have to be changed to set the hardware address length field
+    that must be initialized correctly by the driver (hint: 6 for Ethernet MAC).
+    See the contrib/ports/c16x cs8900 driver as a driver example.
+
+  * netif's have a dhcp field that must be initialized to NULL by the driver.
+    See the contrib/ports/c16x cs8900 driver as a driver example.
+
+(0.5.x) This file has been unmaintained up to 0.6.1. All changes are
+  logged in CVS but have not been explained here.
+
+(0.5.3) Changes since version 0.5.2
+
+  ++ Bugfixes:
+
+  * memp_malloc(MEMP_API_MSG) could fail with multiple application
+    threads because it wasn't protected by semaphores.
+
+  ++ Other changes:
+
+  * struct ip_addr now packed.
+
+  * The name of the time variable in arp.c has been changed to ctime
+    to avoid conflicts with the time() function.
+
+(0.5.2) Changes since version 0.5.1
+
+  ++ New features:
+
+  * A new TCP function, tcp_tmr(), now handles both TCP timers.
+
+  ++ Bugfixes:
+
+  * A bug in tcp_parseopt() could cause the stack to hang because of a
+    malformed TCP option.
+
+  * The address of new connections in the accept() function in the BSD
+    socket library was not handled correctly.
+
+  * pbuf_dechain() did not update the ->tot_len field of the tail.
+
+  * Aborted TCP connections were not handled correctly in all
+    situations.
+
+  ++ Other changes:
+
+  * All protocol header structs are now packed.
+
+  * The ->len field in the tcp_seg structure now counts the actual
+    amount of data, and does not add one for SYN and FIN segments.
+
+(0.5.1) Changes since version 0.5.0
+
+  ++ New features:
+
+  * Possible to run as a user process under Linux.
+
+  * Preliminary support for cross platform packed structs.
+
+  * ARP timer now implemented.
+
+  ++ Bugfixes:
+
+  * TCP output queue length was badly initialized when opening
+    connections.
+
+  * TCP delayed ACKs were not sent correctly.
+
+  * Explicit initialization of BSS segment variables.
+
+  * read() in BSD socket library could drop data.
+
+  * Problems with memory alignment.
+
+  * Situations when all TCP buffers were used could lead to
+    starvation.
+
+  * TCP MSS option wasn't parsed correctly.
+
+  * Problems with UDP checksum calculation.
+
+  * IP multicast address tests had endianess problems.
+
+  * ARP requests had wrong destination hardware address.
+
+  ++ Other changes:
+
+  * struct eth_addr changed from u16_t[3] array to u8_t[6].
+
+  * A ->linkoutput() member was added to struct netif.
+
+  * TCP and UDP ->dest_* struct members where changed to ->remote_*.
+
+  * ntoh* macros are now null definitions for big endian CPUs.
+
+(0.5.0) Changes since version 0.4.2
+
+  ++ New features:
+
+  * Redesigned operating system emulation layer to make porting easier.
+
+  * Better control over TCP output buffers.
+
+  * Documenation added.
+
+  ++ Bugfixes:
+
+  * Locking issues in buffer management.
+
+  * Bugfixes in the sequential API.
+
+  * IP forwarding could cause memory leakage. This has been fixed.
+
+  ++ Other changes:
+
+  * Directory structure somewhat changed; the core/ tree has been
+    collapsed.
+
+(0.4.2) Changes since version 0.4.1
+
+  ++ New features:
+
+  * Experimental ARP implementation added.
+
+  * Skeleton Ethernet driver added.
+
+  * Experimental BSD socket API library added.
+
+  ++ Bugfixes:
+
+  * In very intense situations, memory leakage could occur. This has
+    been fixed.
+
+  ++ Other changes:
+
+  * Variables named "data" and "code" have been renamed in order to
+    avoid name conflicts in certain compilers.
+
+  * Variable++ have in appliciable cases been translated to ++variable
+    since some compilers generate better code in the latter case.
+
+(0.4.1) Changes since version 0.4
+
+  ++ New features:
+
+  * TCP: Connection attempts time out earlier than data
+    transmissions. Nagle algorithm implemented. Push flag set on the
+    last segment in a burst.
+
+  * UDP: experimental support for UDP-Lite extensions.
+
+  ++ Bugfixes:
+
+  * TCP: out of order segments were in some cases handled incorrectly,
+    and this has now been fixed. Delayed acknowledgements was broken
+    in 0.4, has now been fixed. Binding to an address that is in use
+    now results in an error. Reset connections sometimes hung an
+    application; this has been fixed.
+
+  * Checksum calculation sometimes failed for chained pbufs with odd
+    lengths. This has been fixed.
+
+  * API: a lot of bug fixes in the API. The UDP API has been improved
+    and tested. Error reporting and handling has been
+    improved. Logical flaws and race conditions for incoming TCP
+    connections has been found and removed.
+
+  * Memory manager: alignment issues. Reallocating memory sometimes
+    failed, this has been fixed.
+
+  * Generic library: bcopy was flawed and has been fixed.
+
+  ++ Other changes:
+
+  * API: all datatypes has been changed from generic ones such as
+    ints, to specified ones such as u16_t. Functions that return
+    errors now have the correct type (err_t).
+
+  * General: A lot of code cleaned up and debugging code removed. Many
+    portability issues have been fixed.
+
+  * The license was changed; the advertising clause was removed.
+
+  * C64 port added.
+
+  * Thanks: Huge thanks go to Dagan Galarneau, Horst Garnetzke, Petri
+    Kosunen, Mikael Caleres, and Frits Wilmink for reporting and
+    fixing bugs!
+
+(0.4) Changes since version 0.3.1
+
+  * Memory management has been radically changed; instead of
+    allocating memory from a shared heap, memory for objects that are
+    rapidly allocated and deallocated is now kept in pools. Allocation
+    and deallocation from those memory pools is very fast. The shared
+    heap is still present but is used less frequently.
+
+  * The memory, memory pool, and packet buffer subsystems now support
+    4-, 2-, or 1-byte alignment.
+
+  * "Out of memory" situations are handled in a more robust way.
+
+  * Stack usage has been reduced.
+
+  * Easier configuration of lwIP parameters such as memory usage,
+    TTLs, statistics gathering, etc. All configuration parameters are
+    now kept in a single header file "lwipopts.h".
+
+  * The directory structure has been changed slightly so that all
+    architecture specific files are kept under the src/arch
+    hierarchy.
+
+  * Error propagation has been improved, both in the protocol modules
+    and in the API.
+
+  * The code for the RTXC architecture has been implemented, tested
+    and put to use.
+
+  * Bugs have been found and corrected in the TCP, UDP, IP, API, and
+    the Internet checksum modules.
+
+  * Bugs related to porting between a 32-bit and a 16-bit architecture
+    have been found and corrected.
+
+  * The license has been changed slightly to conform more with the
+    original BSD license, including the advertisement clause.
+
+(0.3.1) Changes since version 0.3
+
+  * Fix of a fatal bug in the buffer management. Pbufs with allocated
+    RAM never returned the RAM when the pbuf was deallocated.
+
+  * TCP congestion control, window updates and retransmissions did not
+    work correctly. This has now been fixed.
+
+  * Bugfixes in the API.
+
+(0.3) Changes since version 0.2
+
+  * New and improved directory structure. All include files are now
+    kept in a dedicated include/ directory.
+
+  * The API now has proper error handling. A new function,
+    netconn_err(), now returns an error code for the connection in
+    case of errors.
+
+  * Improvements in the memory management subsystem. The system now
+    keeps a pointer to the lowest free memory block. A new function,
+    mem_malloc2() tries to allocate memory once, and if it fails tries
+    to free some memory and retry the allocation.
+
+  * Much testing has been done with limited memory
+    configurations. lwIP now does a better job when overloaded.
+
+  * Some bugfixes and improvements to the buffer (pbuf) subsystem.
+
+  * Many bugfixes in the TCP code:
+
+    - Fixed a bug in tcp_close().
+
+    - The TCP receive window was incorrectly closed when out of
+      sequence segments was received. This has been fixed.
+
+    - Connections are now timed-out of the FIN-WAIT-2 state.
+
+    - The initial congestion window could in some cases be too
+      large. This has been fixed.
+
+    - The retransmission queue could in some cases be screwed up. This
+      has been fixed.
+
+    - TCP RST flag now handled correctly.
+
+    - Out of sequence data was in some cases never delivered to the
+      application. This has been fixed.
+
+    - Retransmitted segments now contain the correct acknowledgment
+      number and advertised window.
+
+    - TCP retransmission timeout backoffs are not correctly computed
+      (ala BSD). After a number of retransmissions, TCP now gives up
+      the connection.
+
+  * TCP connections now are kept on three lists, one for active
+    connections, one for listening connections, and one for
+    connections that are in TIME-WAIT. This greatly speeds up the fast
+    timeout processing for sending delayed ACKs.
+
+  * TCP now provides proper feedback to the application when a
+    connection has been successfully set up.
+
+  * More comments have been added to the code. The code has also been
+    somewhat cleaned up.
+
+(0.2) Initial public release.

+ 33 - 33
libs/thirdparty/lwip_2.1.2/COPYING → libs/thirdparty/LwIP/COPYING

@@ -1,33 +1,33 @@
-/*
- * Copyright (c) 2001, 2002 Swedish Institute of Computer Science.
- * All rights reserved. 
- * 
- * Redistribution and use in source and binary forms, with or without modification, 
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission. 
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- * 
- * Author: Adam Dunkels <adam@sics.se>
- *
- */
-
-
+/*
+ * Copyright (c) 2001, 2002 Swedish Institute of Computer Science.
+ * All rights reserved. 
+ * 
+ * Redistribution and use in source and binary forms, with or without modification, 
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ * 
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+

+ 4 - 5
libs/thirdparty/lwip_2.1.2/FILES → libs/thirdparty/LwIP/FILES

@@ -1,5 +1,4 @@
-src/      - The source code for the lwIP TCP/IP stack.
-doc/      - The documentation for lwIP.
-test/     - Some code to test whether the sources do what they should.
-
-See also the FILES file in each subdirectory.
+src/      - The source code for the lwIP TCP/IP stack.
+doc/      - The documentation for lwIP.
+
+See also the FILES file in each subdirectory.

+ 89 - 0
libs/thirdparty/LwIP/README

@@ -0,0 +1,89 @@
+INTRODUCTION
+
+lwIP is a small independent implementation of the TCP/IP protocol
+suite that has been developed by Adam Dunkels at the Computer and
+Networks Architectures (CNA) lab at the Swedish Institute of Computer
+Science (SICS).
+
+The focus of the lwIP TCP/IP implementation is to reduce the RAM usage
+while still having a full scale TCP. This making lwIP suitable for use
+in embedded systems with tens of kilobytes of free RAM and room for
+around 40 kilobytes of code ROM.
+
+FEATURES
+
+  * IP (Internet Protocol) including packet forwarding over multiple network
+    interfaces
+  * ICMP (Internet Control Message Protocol) for network maintenance and debugging
+  * IGMP (Internet Group Management Protocol) for multicast traffic management
+  * UDP (User Datagram Protocol) including experimental UDP-lite extensions
+  * TCP (Transmission Control Protocol) with congestion control, RTT estimation
+    and fast recovery/fast retransmit
+  * Specialized raw/native API for enhanced performance
+  * Optional Berkeley-like socket API
+  * DNS (Domain names resolver)
+  * SNMP (Simple Network Management Protocol)
+  * DHCP (Dynamic Host Configuration Protocol)
+  * AUTOIP (for IPv4, conform with RFC 3927)
+  * PPP (Point-to-Point Protocol)
+  * ARP (Address Resolution Protocol) for Ethernet
+
+LICENSE
+
+lwIP is freely available under a BSD license.
+
+DEVELOPMENT
+
+lwIP has grown into an excellent TCP/IP stack for embedded devices,
+and developers using the stack often submit bug fixes, improvements,
+and additions to the stack to further increase its usefulness.
+
+Development of lwIP is hosted on Savannah, a central point for
+software development, maintenance and distribution. Everyone can
+help improve lwIP by use of Savannah's interface, CVS and the
+mailing list. A core team of developers will commit changes to the
+CVS source tree.
+
+The lwIP TCP/IP stack is maintained in the 'lwip' CVS module and
+contributions (such as platform ports) are in the 'contrib' module.
+
+See doc/savannah.txt for details on CVS server access for users and
+developers.
+
+Last night's CVS tar ball can be downloaded from:
+  http://savannah.gnu.org/cvs.backups/lwip.tar.gz [CHANGED - NEEDS FIXING]
+
+The current CVS trees are web-browsable:
+  http://savannah.nongnu.org/cgi-bin/viewcvs/lwip/lwip/
+  http://savannah.nongnu.org/cgi-bin/viewcvs/lwip/contrib/
+
+Submit patches and bugs via the lwIP project page:
+  http://savannah.nongnu.org/projects/lwip/
+
+
+DOCUMENTATION
+
+The original out-dated homepage of lwIP and Adam Dunkels' papers on
+lwIP are at the official lwIP home page:
+  http://www.sics.se/~adam/lwip/
+
+Self documentation of the source code is regularly extracted from the
+current CVS sources and is available from this web page:
+  http://www.nongnu.org/lwip/
+
+There is now a constantly growin wiki about lwIP at
+  http://lwip.wikia.com/wiki/LwIP_Wiki
+
+Also, there are mailing lists you can subscribe at
+  http://savannah.nongnu.org/mail/?group=lwip
+plus searchable archives:
+  http://lists.nongnu.org/archive/html/lwip-users/
+  http://lists.nongnu.org/archive/html/lwip-devel/
+
+Reading Adam's papers, the files in docs/, browsing the source code
+documentation and browsing the mailing list archives is a good way to
+become familiar with the design of lwIP.
+
+Adam Dunkels <adam@sics.se>
+Leon Woestenberg <leon.woestenberg@gmx.net>
+

+ 144 - 0
libs/thirdparty/LwIP/UPGRADING

@@ -0,0 +1,144 @@
+This file lists major changes between release versions that require
+ports or applications to be changed. Use it to update a port or an
+application written for an older version of lwIP to correctly work
+with newer versions.
+
+
+(CVS HEAD)
+
+  * [Enter new changes just after this line - do not remove this line]
+
+  ++ Application changes:
+
+  * Replaced struct ip_addr by typedef ip_addr_t (struct ip_addr is kept for
+    compatibility to old applications, but will be removed in the future).
+
+  * Renamed mem_realloc() to mem_trim() to prevent confusion with realloc()
+
+  +++ Raw API:
+    * Changed the semantics of tcp_close() (since it was rather a
+      shutdown before): Now the application does *NOT* get any calls to the recv
+      callback (aside from NULL/closed) after calling tcp_close()
+
+    * When calling tcp_abort() from a raw API TCP callback function,
+      make sure you return ERR_ABRT to prevent accessing unallocated memory.
+      (ERR_ABRT now means the applicaiton has called tcp_abort!)
+
+  +++ Netconn API:
+    * Changed netconn_receive() and netconn_accept() to return
+      err_t, not a pointer to new data/netconn.
+
+  +++ Socket API:
+    * LWIP_SO_RCVTIMEO: when accept() or recv() time out, they
+      now set errno to EWOULDBLOCK/EAGAIN, not ETIMEDOUT.
+
+    * Added a minimal version of posix fctl() to have a
+      standardised way to set O_NONBLOCK for nonblocking sockets.
+
+  +++ all APIs:
+    * correctly implemented SO(F)_REUSEADDR
+
+  ++ Port changes
+
+  +++ new files:
+
+    * Added 4 new files: def.c, timers.c, timers.h, tcp_impl.h:
+
+    * Moved stack-internal parts of tcp.h to tcp_impl.h, tcp.h now only contains
+      the actual application programmer's API
+  
+    * Separated timer implementation from sys.h/.c, moved to timers.h/.c;
+      Added timer implementation for NO_SYS==1, set NO_SYS_NO_TIMERS==1 if you
+      still want to use your own timer implementation for NO_SYS==0 (as before).
+
+  +++ sys layer:
+
+    * Converted mbox- and semaphore-functions to take pointers to sys_mbox_t/
+      sys_sem_t;
+
+    * Converted sys_mbox_new/sys_sem_new to take pointers and return err_t;
+
+    * Added Mutex concept in sys_arch (define LWIP_COMPAT_MUTEX to let sys.h use
+      binary semaphores instead of mutexes - as before)
+
+  +++ new options:
+
+     * Don't waste memory when chaining segments, added option TCP_OVERSIZE to
+       prevent creating many small pbufs when calling tcp_write with many small
+       blocks of data. Instead, pbufs are allocated larger than needed and the
+       space is used for later calls to tcp_write.
+
+     * Added LWIP_NETIF_TX_SINGLE_PBUF to always copy to try to create single pbufs
+       in tcp_write/udp_send.
+
+    * Added an additional option LWIP_ETHERNET to support ethernet without ARP
+      (necessary for pure PPPoE)
+
+    * Add MEMP_SEPARATE_POOLS to place memory pools in separate arrays. This may
+      be used to place these pools into user-defined memory by using external
+      declaration.
+
+    * Added TCP_SNDQUEUELOWAT corresponding to TCP_SNDLOWAT
+
+  +++ new pools:
+
+     * Netdb uses a memp pool for allocating memory when getaddrinfo() is called,
+       so MEMP_NUM_NETDB has to be set accordingly.
+
+     * DNS_LOCAL_HOSTLIST_IS_DYNAMIC uses a memp pool instead of the heap, so
+       MEMP_NUM_LOCALHOSTLIST has to be set accordingly.
+
+     * Snmp-agent uses a memp pools instead of the heap, so MEMP_NUM_SNMP_* have
+       to be set accordingly.
+
+     * PPPoE uses a MEMP pool instead of the heap, so MEMP_NUM_PPPOE_INTERFACES
+       has to be set accordingly
+
+  * Integrated loopif into netif.c - loopif does not have to be created by the
+    port any more, just define LWIP_HAVE_LOOPIF to 1.
+
+  * Added define LWIP_RAND() for lwip-wide randomization (needs to be defined
+    in cc.h, e.g. used by igmp)
+
+  * Added printf-formatter X8_F to printf u8_t as hex
+
+  * The heap now may be moved to user-defined memory by defining
+    LWIP_RAM_HEAP_POINTER as a void pointer to that memory's address
+
+  * added autoip_set_struct() and dhcp_set_struct() to let autoip and dhcp work
+    with user-allocated structs instead of calling mem_malloc
+
+  * Added const char* name to mem- and memp-stats for easier debugging.
+
+  * Calculate the TCP/UDP checksum while copying to only fetch data once:
+    Define LWIP_CHKSUM_COPY to a memcpy-like function that returns the checksum
+
+  * Added SO_REUSE_RXTOALL to pass received UDP broadcast/multicast packets to
+    more than one pcb.
+
+  * Changed the semantics of ARP_QUEUEING==0: ARP_QUEUEING now cannot be turned
+    off any more, if this is set to 0, only one packet (the most recent one) is
+    queued (like demanded by RFC 1122).
+
+  
+  ++ Major bugfixes/improvements
+
+  * Implemented tcp_shutdown() to only shut down one end of a connection
+  * Implemented shutdown() at socket- and netconn-level
+  * Added errorset support to select() + improved select speed overhead
+  * Merged pppd to v2.3.11 (including some backported bugfixes from 2.4.x)
+  * Added timer implementation for NO_SYS==1 (may be disabled with NO_SYS_NO_TIMERS==1
+  * Use macros defined in ip_addr.h to work with IP addresses
+  * Implemented many nonblocking socket/netconn functions
+  * Fixed ARP input processing: only add a new entry if a request was directed as us
+  * mem_realloc() to mem_trim() to prevent confusion with realloc()
+  * Some improvements for AutoIP (don't route/forward link-local addresses, don't break
+    existing connections when assigning a routable address)
+  * Correctly handle remote side overrunning our rcv_wnd in ooseq case
+  * Removed packing from ip_addr_t, the packed version is now only used in protocol headers
+  * Corrected PBUF_POOL_BUFSIZE for ports where ETH_PAD_SIZE > 0
+  * Added support for static ARP table entries
+
+(STABLE-1.3.2)
+
+  * initial version of this file

+ 511 - 0
libs/thirdparty/LwIP/doc rawapi.txt

@@ -0,0 +1,511 @@
+Raw TCP/IP interface for lwIP
+
+Authors: Adam Dunkels, Leon Woestenberg, Christiaan Simons
+
+lwIP provides three Application Program's Interfaces (APIs) for programs
+to use for communication with the TCP/IP code:
+* low-level "core" / "callback" or "raw" API.
+* higher-level "sequential" API.
+* BSD-style socket API.
+
+The sequential API provides a way for ordinary, sequential, programs
+to use the lwIP stack. It is quite similar to the BSD socket API. The
+model of execution is based on the blocking open-read-write-close
+paradigm. Since the TCP/IP stack is event based by nature, the TCP/IP
+code and the application program must reside in different execution
+contexts (threads).
+
+The socket API is a compatibility API for existing applications,
+currently it is built on top of the sequential API. It is meant to
+provide all functions needed to run socket API applications running
+on other platforms (e.g. unix / windows etc.). However, due to limitations
+in the specification of this API, there might be incompatibilities
+that require small modifications of existing programs.
+
+** Threading
+
+lwIP started targeting single-threaded environments. When adding multi-
+threading support, instead of making the core thread-safe, another
+approach was chosen: there is one main thread running the lwIP core
+(also known as the "tcpip_thread"). The raw API may only be used from
+this thread! Application threads using the sequential- or socket API
+communicate with this main thread through message passing.
+
+      As such, the list of functions that may be called from
+      other threads or an ISR is very limited! Only functions
+      from these API header files are thread-safe:
+      - api.h
+      - netbuf.h
+      - netdb.h
+      - netifapi.h
+      - sockets.h
+      - sys.h
+
+      Additionaly, memory (de-)allocation functions may be
+      called from multiple threads (not ISR!) with NO_SYS=0
+      since they are protected by SYS_LIGHTWEIGHT_PROT and/or
+      semaphores.
+
+      Only since 1.3.0, if SYS_LIGHTWEIGHT_PROT is set to 1
+      and LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT is set to 1,
+      pbuf_free() may also be called from another thread or
+      an ISR (since only then, mem_free - for PBUF_RAM - may
+      be called from an ISR: otherwise, the HEAP is only
+      protected by semaphores).
+      
+
+** The remainder of this document discusses the "raw" API. **
+
+The raw TCP/IP interface allows the application program to integrate
+better with the TCP/IP code. Program execution is event based by
+having callback functions being called from within the TCP/IP
+code. The TCP/IP code and the application program both run in the same
+thread. The sequential API has a much higher overhead and is not very
+well suited for small systems since it forces a multithreaded paradigm
+on the application.
+
+The raw TCP/IP interface is not only faster in terms of code execution
+time but is also less memory intensive. The drawback is that program
+development is somewhat harder and application programs written for
+the raw TCP/IP interface are more difficult to understand. Still, this
+is the preferred way of writing applications that should be small in
+code size and memory usage.
+
+Both APIs can be used simultaneously by different application
+programs. In fact, the sequential API is implemented as an application
+program using the raw TCP/IP interface.
+
+--- Callbacks
+
+Program execution is driven by callbacks. Each callback is an ordinary
+C function that is called from within the TCP/IP code. Every callback
+function is passed the current TCP or UDP connection state as an
+argument. Also, in order to be able to keep program specific state,
+the callback functions are called with a program specified argument
+that is independent of the TCP/IP state.
+
+The function for setting the application connection state is:
+
+- void tcp_arg(struct tcp_pcb *pcb, void *arg)
+
+  Specifies the program specific state that should be passed to all
+  other callback functions. The "pcb" argument is the current TCP
+  connection control block, and the "arg" argument is the argument
+  that will be passed to the callbacks.
+
+  
+--- TCP connection setup
+
+The functions used for setting up connections is similar to that of
+the sequential API and of the BSD socket API. A new TCP connection
+identifier (i.e., a protocol control block - PCB) is created with the
+tcp_new() function. This PCB can then be either set to listen for new
+incoming connections or be explicitly connected to another host.
+
+- struct tcp_pcb *tcp_new(void)
+
+  Creates a new connection identifier (PCB). If memory is not
+  available for creating the new pcb, NULL is returned.
+
+- err_t tcp_bind(struct tcp_pcb *pcb, ip_addr_t *ipaddr,
+                 u16_t port)
+
+  Binds the pcb to a local IP address and port number. The IP address
+  can be specified as IP_ADDR_ANY in order to bind the connection to
+  all local IP addresses.
+
+  If another connection is bound to the same port, the function will
+  return ERR_USE, otherwise ERR_OK is returned.
+
+- struct tcp_pcb *tcp_listen(struct tcp_pcb *pcb)
+
+  Commands a pcb to start listening for incoming connections. When an
+  incoming connection is accepted, the function specified with the
+  tcp_accept() function will be called. The pcb will have to be bound
+  to a local port with the tcp_bind() function.
+
+  The tcp_listen() function returns a new connection identifier, and
+  the one passed as an argument to the function will be
+  deallocated. The reason for this behavior is that less memory is
+  needed for a connection that is listening, so tcp_listen() will
+  reclaim the memory needed for the original connection and allocate a
+  new smaller memory block for the listening connection.
+
+  tcp_listen() may return NULL if no memory was available for the
+  listening connection. If so, the memory associated with the pcb
+  passed as an argument to tcp_listen() will not be deallocated.
+
+- struct tcp_pcb *tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog)
+
+  Same as tcp_listen, but limits the number of outstanding connections
+  in the listen queue to the value specified by the backlog argument.
+  To use it, your need to set TCP_LISTEN_BACKLOG=1 in your lwipopts.h.
+
+- void tcp_accepted(struct tcp_pcb *pcb)
+
+  Inform lwIP that an incoming connection has been accepted. This would
+  usually be called from the accept callback. This allows lwIP to perform
+  housekeeping tasks, such as allowing further incoming connections to be
+  queued in the listen backlog.
+  ATTENTION: the PCB passed in must be the listening pcb, not the pcb passed
+  into the accept callback!
+
+- void tcp_accept(struct tcp_pcb *pcb,
+                  err_t (* accept)(void *arg, struct tcp_pcb *newpcb,
+                                   err_t err))
+
+  Specified the callback function that should be called when a new
+  connection arrives on a listening connection.
+
+- err_t tcp_connect(struct tcp_pcb *pcb, ip_addr_t *ipaddr,
+                    u16_t port, err_t (* connected)(void *arg,
+                                                    struct tcp_pcb *tpcb,
+                                                    err_t err));
+
+  Sets up the pcb to connect to the remote host and sends the
+  initial SYN segment which opens the connection. 
+
+  The tcp_connect() function returns immediately; it does not wait for
+  the connection to be properly setup. Instead, it will call the
+  function specified as the fourth argument (the "connected" argument)
+  when the connection is established. If the connection could not be
+  properly established, either because the other host refused the
+  connection or because the other host didn't answer, the "err"
+  callback function of this pcb (registered with tcp_err, see below)
+  will be called.
+
+  The tcp_connect() function can return ERR_MEM if no memory is
+  available for enqueueing the SYN segment. If the SYN indeed was
+  enqueued successfully, the tcp_connect() function returns ERR_OK.
+
+
+--- Sending TCP data
+
+TCP data is sent by enqueueing the data with a call to
+tcp_write(). When the data is successfully transmitted to the remote
+host, the application will be notified with a call to a specified
+callback function.
+
+- err_t tcp_write(struct tcp_pcb *pcb, const void *dataptr, u16_t len,
+                  u8_t apiflags)
+
+  Enqueues the data pointed to by the argument dataptr. The length of
+  the data is passed as the len parameter. The apiflags can be one or more of:
+  - TCP_WRITE_FLAG_COPY: indicates whether the new memory should be allocated
+    for the data to be copied into. If this flag is not given, no new memory
+    should be allocated and the data should only be referenced by pointer. This
+    also means that the memory behind dataptr must not change until the data is
+    ACKed by the remote host
+  - TCP_WRITE_FLAG_MORE: indicates that more data follows. If this is given,
+    the PSH flag is set in the last segment created by this call to tcp_write.
+    If this flag is given, the PSH flag is not set.
+
+  The tcp_write() function will fail and return ERR_MEM if the length
+  of the data exceeds the current send buffer size or if the length of
+  the queue of outgoing segment is larger than the upper limit defined
+  in lwipopts.h. The number of bytes available in the output queue can
+  be retrieved with the tcp_sndbuf() function.
+
+  The proper way to use this function is to call the function with at
+  most tcp_sndbuf() bytes of data. If the function returns ERR_MEM,
+  the application should wait until some of the currently enqueued
+  data has been successfully received by the other host and try again.
+
+- void tcp_sent(struct tcp_pcb *pcb,
+                err_t (* sent)(void *arg, struct tcp_pcb *tpcb,
+                u16_t len))
+
+  Specifies the callback function that should be called when data has
+  successfully been received (i.e., acknowledged) by the remote
+  host. The len argument passed to the callback function gives the
+  amount bytes that was acknowledged by the last acknowledgment.
+
+  
+--- Receiving TCP data
+
+TCP data reception is callback based - an application specified
+callback function is called when new data arrives. When the
+application has taken the data, it has to call the tcp_recved()
+function to indicate that TCP can advertise increase the receive
+window.
+
+- void tcp_recv(struct tcp_pcb *pcb,
+                err_t (* recv)(void *arg, struct tcp_pcb *tpcb,
+                               struct pbuf *p, err_t err))
+
+  Sets the callback function that will be called when new data
+  arrives. The callback function will be passed a NULL pbuf to
+  indicate that the remote host has closed the connection. If
+  there are no errors and the callback function is to return
+  ERR_OK, then it must free the pbuf. Otherwise, it must not
+  free the pbuf so that lwIP core code can store it.
+
+- void tcp_recved(struct tcp_pcb *pcb, u16_t len)
+
+  Must be called when the application has received the data. The len
+  argument indicates the length of the received data.
+
+
+--- Application polling
+
+When a connection is idle (i.e., no data is either transmitted or
+received), lwIP will repeatedly poll the application by calling a
+specified callback function. This can be used either as a watchdog
+timer for killing connections that have stayed idle for too long, or
+as a method of waiting for memory to become available. For instance,
+if a call to tcp_write() has failed because memory wasn't available,
+the application may use the polling functionality to call tcp_write()
+again when the connection has been idle for a while.
+
+- void tcp_poll(struct tcp_pcb *pcb, 
+                err_t (* poll)(void *arg, struct tcp_pcb *tpcb),
+                u8_t interval)
+
+  Specifies the polling interval and the callback function that should
+  be called to poll the application. The interval is specified in
+  number of TCP coarse grained timer shots, which typically occurs
+  twice a second. An interval of 10 means that the application would
+  be polled every 5 seconds.
+
+
+--- Closing and aborting connections
+
+- err_t tcp_close(struct tcp_pcb *pcb)
+
+  Closes the connection. The function may return ERR_MEM if no memory
+  was available for closing the connection. If so, the application
+  should wait and try again either by using the acknowledgment
+  callback or the polling functionality. If the close succeeds, the
+  function returns ERR_OK.
+
+  The pcb is deallocated by the TCP code after a call to tcp_close(). 
+
+- void tcp_abort(struct tcp_pcb *pcb)
+
+  Aborts the connection by sending a RST (reset) segment to the remote
+  host. The pcb is deallocated. This function never fails.
+
+  ATTENTION: When calling this from one of the TCP callbacks, make
+  sure you always return ERR_ABRT (and never return ERR_ABRT otherwise
+  or you will risk accessing deallocated memory or memory leaks!
+
+
+If a connection is aborted because of an error, the application is
+alerted of this event by the err callback. Errors that might abort a
+connection are when there is a shortage of memory. The callback
+function to be called is set using the tcp_err() function.
+
+- void tcp_err(struct tcp_pcb *pcb, void (* err)(void *arg,
+       err_t err))
+
+  The error callback function does not get the pcb passed to it as a
+  parameter since the pcb may already have been deallocated.
+
+
+--- Lower layer TCP interface
+
+TCP provides a simple interface to the lower layers of the
+system. During system initialization, the function tcp_init() has
+to be called before any other TCP function is called. When the system
+is running, the two timer functions tcp_fasttmr() and tcp_slowtmr()
+must be called with regular intervals. The tcp_fasttmr() should be
+called every TCP_FAST_INTERVAL milliseconds (defined in tcp.h) and
+tcp_slowtmr() should be called every TCP_SLOW_INTERVAL milliseconds. 
+
+
+--- UDP interface
+
+The UDP interface is similar to that of TCP, but due to the lower
+level of complexity of UDP, the interface is significantly simpler.
+
+- struct udp_pcb *udp_new(void)
+
+  Creates a new UDP pcb which can be used for UDP communication. The
+  pcb is not active until it has either been bound to a local address
+  or connected to a remote address.
+
+- void udp_remove(struct udp_pcb *pcb)
+
+  Removes and deallocates the pcb.  
+  
+- err_t udp_bind(struct udp_pcb *pcb, ip_addr_t *ipaddr,
+                 u16_t port)
+
+  Binds the pcb to a local address. The IP-address argument "ipaddr"
+  can be IP_ADDR_ANY to indicate that it should listen to any local IP
+  address. The function currently always return ERR_OK.
+
+- err_t udp_connect(struct udp_pcb *pcb, ip_addr_t *ipaddr,
+                    u16_t port)
+
+  Sets the remote end of the pcb. This function does not generate any
+  network traffic, but only set the remote address of the pcb.
+
+- err_t udp_disconnect(struct udp_pcb *pcb)
+
+  Remove the remote end of the pcb. This function does not generate
+  any network traffic, but only removes the remote address of the pcb.
+
+- err_t udp_send(struct udp_pcb *pcb, struct pbuf *p)
+
+  Sends the pbuf p. The pbuf is not deallocated.
+
+- void udp_recv(struct udp_pcb *pcb,
+                void (* recv)(void *arg, struct udp_pcb *upcb,
+                                         struct pbuf *p,
+                                         ip_addr_t *addr,
+                                         u16_t port),
+                              void *recv_arg)
+
+  Specifies a callback function that should be called when a UDP
+  datagram is received.
+  
+
+--- System initalization
+
+A truly complete and generic sequence for initializing the lwip stack
+cannot be given because it depends on the build configuration (lwipopts.h)
+and additional initializations for your runtime environment (e.g. timers).
+
+We can give you some idea on how to proceed when using the raw API.
+We assume a configuration using a single Ethernet netif and the
+UDP and TCP transport layers, IPv4 and the DHCP client.
+
+Call these functions in the order of appearance:
+
+- stats_init()
+
+  Clears the structure where runtime statistics are gathered.
+
+- sys_init()
+  
+  Not of much use since we set the NO_SYS 1 option in lwipopts.h,
+  to be called for easy configuration changes.
+
+- mem_init()
+
+  Initializes the dynamic memory heap defined by MEM_SIZE.
+
+- memp_init()
+
+  Initializes the memory pools defined by MEMP_NUM_x.
+
+- pbuf_init()
+
+  Initializes the pbuf memory pool defined by PBUF_POOL_SIZE.
+  
+- etharp_init()
+
+  Initializes the ARP table and queue.
+  Note: you must call etharp_tmr at a ARP_TMR_INTERVAL (5 seconds) regular interval
+  after this initialization.
+
+- ip_init()
+
+  Doesn't do much, it should be called to handle future changes.
+
+- udp_init()
+
+  Clears the UDP PCB list.
+
+- tcp_init()
+
+  Clears the TCP PCB list and clears some internal TCP timers.
+  Note: you must call tcp_fasttmr() and tcp_slowtmr() at the
+  predefined regular intervals after this initialization. 
+  
+- netif_add(struct netif *netif, ip_addr_t *ipaddr,
+            ip_addr_t *netmask, ip_addr_t *gw,
+            void *state, err_t (* init)(struct netif *netif),
+            err_t (* input)(struct pbuf *p, struct netif *netif))
+
+  Adds your network interface to the netif_list. Allocate a struct
+  netif and pass a pointer to this structure as the first argument.
+  Give pointers to cleared ip_addr structures when using DHCP,
+  or fill them with sane numbers otherwise. The state pointer may be NULL.
+
+  The init function pointer must point to a initialization function for
+  your ethernet netif interface. The following code illustrates it's use.
+  
+  err_t netif_if_init(struct netif *netif)
+  {
+    u8_t i;
+    
+    for(i = 0; i < ETHARP_HWADDR_LEN; i++) netif->hwaddr[i] = some_eth_addr[i];
+    init_my_eth_device();
+    return ERR_OK;
+  }
+  
+  For ethernet drivers, the input function pointer must point to the lwip
+  function ethernet_input() declared in "netif/etharp.h". Other drivers
+  must use ip_input() declared in "lwip/ip.h".
+  
+- netif_set_default(struct netif *netif)
+
+  Registers the default network interface.
+
+- netif_set_up(struct netif *netif)
+
+  When the netif is fully configured this function must be called.
+
+- dhcp_start(struct netif *netif)
+
+  Creates a new DHCP client for this interface on the first call.
+  Note: you must call dhcp_fine_tmr() and dhcp_coarse_tmr() at
+  the predefined regular intervals after starting the client.
+  
+  You can peek in the netif->dhcp struct for the actual DHCP status.
+
+
+--- Optimalization hints
+
+The first thing you want to optimize is the lwip_standard_checksum()
+routine from src/core/inet.c. You can override this standard
+function with the #define LWIP_CHKSUM <your_checksum_routine>.
+
+There are C examples given in inet.c or you might want to
+craft an assembly function for this. RFC1071 is a good
+introduction to this subject.
+
+Other significant improvements can be made by supplying
+assembly or inline replacements for htons() and htonl()
+if you're using a little-endian architecture.
+#define LWIP_PLATFORM_BYTESWAP 1
+#define LWIP_PLATFORM_HTONS(x) <your_htons>
+#define LWIP_PLATFORM_HTONL(x) <your_htonl>
+
+Check your network interface driver if it reads at
+a higher speed than the maximum wire-speed. If the
+hardware isn't serviced frequently and fast enough
+buffer overflows are likely to occur.
+
+E.g. when using the cs8900 driver, call cs8900if_service(ethif)
+as frequently as possible. When using an RTOS let the cs8900 interrupt
+wake a high priority task that services your driver using a binary
+semaphore or event flag. Some drivers might allow additional tuning
+to match your application and network.
+
+For a production release it is recommended to set LWIP_STATS to 0.
+Note that speed performance isn't influenced much by simply setting
+high values to the memory options.
+
+For more optimization hints take a look at the lwIP wiki.
+
+--- Zero-copy MACs
+
+To achieve zero-copy on transmit, the data passed to the raw API must
+remain unchanged until sent. Because the send- (or write-)functions return
+when the packets have been enqueued for sending, data must be kept stable
+after that, too.
+
+This implies that PBUF_RAM/PBUF_POOL pbufs passed to raw-API send functions
+must *not* be reused by the application unless their ref-count is 1.
+
+For no-copy pbufs (PBUF_ROM/PBUF_REF), data must be kept unchanged, too,
+but the stack/driver will/must copy PBUF_REF'ed data when enqueueing, while
+PBUF_ROM-pbufs are just enqueued (as ROM-data is expected to never change).
+
+Also, data passed to tcp_write without the copy-flag must not be changed!
+
+Therefore, be careful which type of PBUF you use and if you copy TCP data
+or not!

+ 6 - 9
libs/thirdparty/lwip_2.1.2/doc/FILES → libs/thirdparty/LwIP/doc/FILES

@@ -1,9 +1,6 @@
-doxygen/       - Configuration files and scripts to create the lwIP doxygen source
-                 documentation (found at http://www.nongnu.org/lwip/)
-
-savannah.txt   - How to obtain the current development source code.
-contrib.txt    - How to contribute to lwIP as a developer.
-rawapi.txt     - The documentation for the core API of lwIP.
-                 Also provides an overview about the other APIs and multithreading.
-sys_arch.txt   - The documentation for a system abstraction layer of lwIP.
-ppp.txt        - Documentation of the PPP interface for lwIP.
+savannah.txt   - How to obtain the current development source code.
+contrib.txt    - How to contribute to lwIP as a developer.
+rawapi.txt     - The documentation for the core API of lwIP.
+                 Also provides an overview about the other APIs and multithreading.
+snmp_agent.txt - The documentation for the lwIP SNMP agent.
+sys_arch.txt   - The documentation for a system abstraction layer of lwIP.

+ 63 - 58
libs/thirdparty/lwip_2.1.2/doc/contrib.txt → libs/thirdparty/LwIP/doc/contrib.txt

@@ -1,58 +1,63 @@
-1 Introduction
-
-This document describes some guidelines for people participating
-in lwIP development.
-
-2 How to contribute to lwIP
-
-Here is a short list of suggestions to anybody working with lwIP and 
-trying to contribute bug reports, fixes, enhancements, platform ports etc.
-First of all as you may already know lwIP is a volunteer project so feedback
-to fixes or questions might often come late. Hopefully the bug and patch tracking 
-features of Savannah help us not lose users' input.
-
-2.1 Source code style:
-
-1. do not use tabs.
-2. indentation is two spaces per level (i.e. per tab).
-3. end debug messages with a trailing newline (\n).
-4. one space between keyword and opening bracket.
-5. no space between function and opening bracket.
-6. one space and no newline before opening curly braces of a block.
-7. closing curly brace on a single line.
-8. spaces surrounding assignment and comparisons.
-9. don't initialize static and/or global variables to zero, the compiler takes care of that.
-10. use current source code style as further reference.
-
-2.2 Source code documentation style:
-
-1. JavaDoc compliant and Doxygen compatible.
-2. Function documentation above functions in .c files, not .h files.
-   (This forces you to synchronize documentation and implementation.)
-3. Use current documentation style as further reference.
- 
-2.3 Bug reports and patches:
-
-1. Make sure you are reporting bugs or send patches against the latest
-   sources. (From the latest release and/or the current Git sources.)
-2. If you think you found a bug make sure it's not already filed in the
-   bugtracker at Savannah.
-3. If you have a fix put the patch on Savannah. If it is a patch that affects
-   both core and arch specific stuff please separate them so that the core can
-   be applied separately while leaving the other patch 'open'. The preferred way
-   is to NOT touch archs you can't test and let maintainers take care of them.
-   This is a good way to see if they are used at all - the same goes for unix
-   netifs except tapif.
-4. Do not file a bug and post a fix to it to the patch area. Either a bug report
-   or a patch will be enough.
-   If you correct an existing bug then attach the patch to the bug rather than creating a new entry in the patch area.
-5. Patches should be specific to a single change or to related changes. Do not mix bugfixes with spelling and other
-   trivial fixes unless the bugfix is trivial too. Do not reorganize code and rename identifiers in the same patch you
-   change behaviour if not necessary. A patch is easier to read and understand if it's to the point and short than
-   if it's not to the point and long :) so the chances for it to be applied are greater. 
-
-2.4 Platform porters:
-
-1. If you have ported lwIP to a platform (an OS, a uC/processor or a combination of these) and
-   you think it could benefit others[1] you might want discuss this on the mailing list. You
-   can also ask for Git access to submit and maintain your port in the contrib Git module.
+1 Introduction
+
+This document describes some guidelines for people participating
+in lwIP development.
+
+2 How to contribute to lwIP
+
+Here is a short list of suggestions to anybody working with lwIP and 
+trying to contribute bug reports, fixes, enhancements, platform ports etc.
+First of all as you may already know lwIP is a volunteer project so feedback
+to fixes or questions might often come late. Hopefully the bug and patch tracking 
+features of Savannah help us not lose users' input.
+
+2.1 Source code style:
+
+1. do not use tabs.
+2. indentation is two spaces per level (i.e. per tab).
+3. end debug messages with a trailing newline (\n).
+4. one space between keyword and opening bracket.
+5. no space between function and opening bracket.
+6. one space and no newline before opening curly braces of a block.
+7. closing curly brace on a single line.
+8. spaces surrounding assignment and comparisons.
+9. don't initialize static and/or global variables to zero, the compiler takes care of that.
+10. use current source code style as further reference.
+
+2.2 Source code documentation style:
+
+1. JavaDoc compliant and Doxygen compatible.
+2. Function documentation above functions in .c files, not .h files.
+   (This forces you to synchronize documentation and implementation.)
+3. Use current documentation style as further reference.
+ 
+2.3 Bug reports and patches:
+
+1. Make sure you are reporting bugs or send patches against the latest
+   sources. (From the latest release and/or the current CVS sources.)
+2. If you think you found a bug make sure it's not already filed in the
+   bugtracker at Savannah.
+3. If you have a fix put the patch on Savannah. If it is a patch that affects
+   both core and arch specific stuff please separate them so that the core can
+   be applied separately while leaving the other patch 'open'. The prefered way
+   is to NOT touch archs you can't test and let maintainers take care of them.
+   This is a good way to see if they are used at all - the same goes for unix
+   netifs except tapif.
+4. Do not file a bug and post a fix to it to the patch area. Either a bug report
+   or a patch will be enough.
+   If you correct an existing bug then attach the patch to the bug rather than creating a new entry in the patch area.
+5. Trivial patches (compiler warning, indentation and spelling fixes or anything obvious which takes a line or two)
+   can go to the lwip-users list. This is still the fastest way of interaction and the list is not so crowded
+   as to allow for loss of fixes. Putting bugs on Savannah and subsequently closing them is too much an overhead
+   for reporting a compiler warning fix.
+6. Patches should be specific to a single change or to related changes.Do not mix bugfixes with spelling and other
+   trivial fixes unless the bugfix is trivial too.Do not reorganize code and rename identifiers in the same patch you
+   change behaviour if not necessary.A patch is easier to read and understand if it's to the point and short than
+   if it's not to the point and long :) so the chances for it to be applied are greater. 
+
+2.4 Platform porters:
+
+1. If you have ported lwIP to a platform (an OS, a uC/processor or a combination of these) and
+   you think it could benefit others[1] you might want discuss this on the mailing list. You
+   can also ask for CVS access to submit and maintain your port in the contrib CVS module.
+   

+ 511 - 0
libs/thirdparty/LwIP/doc/rawapi.txt

@@ -0,0 +1,511 @@
+Raw TCP/IP interface for lwIP
+
+Authors: Adam Dunkels, Leon Woestenberg, Christiaan Simons
+
+lwIP provides three Application Program's Interfaces (APIs) for programs
+to use for communication with the TCP/IP code:
+* low-level "core" / "callback" or "raw" API.
+* higher-level "sequential" API.
+* BSD-style socket API.
+
+The sequential API provides a way for ordinary, sequential, programs
+to use the lwIP stack. It is quite similar to the BSD socket API. The
+model of execution is based on the blocking open-read-write-close
+paradigm. Since the TCP/IP stack is event based by nature, the TCP/IP
+code and the application program must reside in different execution
+contexts (threads).
+
+The socket API is a compatibility API for existing applications,
+currently it is built on top of the sequential API. It is meant to
+provide all functions needed to run socket API applications running
+on other platforms (e.g. unix / windows etc.). However, due to limitations
+in the specification of this API, there might be incompatibilities
+that require small modifications of existing programs.
+
+** Threading
+
+lwIP started targeting single-threaded environments. When adding multi-
+threading support, instead of making the core thread-safe, another
+approach was chosen: there is one main thread running the lwIP core
+(also known as the "tcpip_thread"). The raw API may only be used from
+this thread! Application threads using the sequential- or socket API
+communicate with this main thread through message passing.
+
+      As such, the list of functions that may be called from
+      other threads or an ISR is very limited! Only functions
+      from these API header files are thread-safe:
+      - api.h
+      - netbuf.h
+      - netdb.h
+      - netifapi.h
+      - sockets.h
+      - sys.h
+
+      Additionaly, memory (de-)allocation functions may be
+      called from multiple threads (not ISR!) with NO_SYS=0
+      since they are protected by SYS_LIGHTWEIGHT_PROT and/or
+      semaphores.
+
+      Only since 1.3.0, if SYS_LIGHTWEIGHT_PROT is set to 1
+      and LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT is set to 1,
+      pbuf_free() may also be called from another thread or
+      an ISR (since only then, mem_free - for PBUF_RAM - may
+      be called from an ISR: otherwise, the HEAP is only
+      protected by semaphores).
+      
+
+** The remainder of this document discusses the "raw" API. **
+
+The raw TCP/IP interface allows the application program to integrate
+better with the TCP/IP code. Program execution is event based by
+having callback functions being called from within the TCP/IP
+code. The TCP/IP code and the application program both run in the same
+thread. The sequential API has a much higher overhead and is not very
+well suited for small systems since it forces a multithreaded paradigm
+on the application.
+
+The raw TCP/IP interface is not only faster in terms of code execution
+time but is also less memory intensive. The drawback is that program
+development is somewhat harder and application programs written for
+the raw TCP/IP interface are more difficult to understand. Still, this
+is the preferred way of writing applications that should be small in
+code size and memory usage.
+
+Both APIs can be used simultaneously by different application
+programs. In fact, the sequential API is implemented as an application
+program using the raw TCP/IP interface.
+
+--- Callbacks
+
+Program execution is driven by callbacks. Each callback is an ordinary
+C function that is called from within the TCP/IP code. Every callback
+function is passed the current TCP or UDP connection state as an
+argument. Also, in order to be able to keep program specific state,
+the callback functions are called with a program specified argument
+that is independent of the TCP/IP state.
+
+The function for setting the application connection state is:
+
+- void tcp_arg(struct tcp_pcb *pcb, void *arg)
+
+  Specifies the program specific state that should be passed to all
+  other callback functions. The "pcb" argument is the current TCP
+  connection control block, and the "arg" argument is the argument
+  that will be passed to the callbacks.
+
+  
+--- TCP connection setup
+
+The functions used for setting up connections is similar to that of
+the sequential API and of the BSD socket API. A new TCP connection
+identifier (i.e., a protocol control block - PCB) is created with the
+tcp_new() function. This PCB can then be either set to listen for new
+incoming connections or be explicitly connected to another host.
+
+- struct tcp_pcb *tcp_new(void)
+
+  Creates a new connection identifier (PCB). If memory is not
+  available for creating the new pcb, NULL is returned.
+
+- err_t tcp_bind(struct tcp_pcb *pcb, ip_addr_t *ipaddr,
+                 u16_t port)
+
+  Binds the pcb to a local IP address and port number. The IP address
+  can be specified as IP_ADDR_ANY in order to bind the connection to
+  all local IP addresses.
+
+  If another connection is bound to the same port, the function will
+  return ERR_USE, otherwise ERR_OK is returned.
+
+- struct tcp_pcb *tcp_listen(struct tcp_pcb *pcb)
+
+  Commands a pcb to start listening for incoming connections. When an
+  incoming connection is accepted, the function specified with the
+  tcp_accept() function will be called. The pcb will have to be bound
+  to a local port with the tcp_bind() function.
+
+  The tcp_listen() function returns a new connection identifier, and
+  the one passed as an argument to the function will be
+  deallocated. The reason for this behavior is that less memory is
+  needed for a connection that is listening, so tcp_listen() will
+  reclaim the memory needed for the original connection and allocate a
+  new smaller memory block for the listening connection.
+
+  tcp_listen() may return NULL if no memory was available for the
+  listening connection. If so, the memory associated with the pcb
+  passed as an argument to tcp_listen() will not be deallocated.
+
+- struct tcp_pcb *tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog)
+
+  Same as tcp_listen, but limits the number of outstanding connections
+  in the listen queue to the value specified by the backlog argument.
+  To use it, your need to set TCP_LISTEN_BACKLOG=1 in your lwipopts.h.
+
+- void tcp_accepted(struct tcp_pcb *pcb)
+
+  Inform lwIP that an incoming connection has been accepted. This would
+  usually be called from the accept callback. This allows lwIP to perform
+  housekeeping tasks, such as allowing further incoming connections to be
+  queued in the listen backlog.
+  ATTENTION: the PCB passed in must be the listening pcb, not the pcb passed
+  into the accept callback!
+
+- void tcp_accept(struct tcp_pcb *pcb,
+                  err_t (* accept)(void *arg, struct tcp_pcb *newpcb,
+                                   err_t err))
+
+  Specified the callback function that should be called when a new
+  connection arrives on a listening connection.
+
+- err_t tcp_connect(struct tcp_pcb *pcb, ip_addr_t *ipaddr,
+                    u16_t port, err_t (* connected)(void *arg,
+                                                    struct tcp_pcb *tpcb,
+                                                    err_t err));
+
+  Sets up the pcb to connect to the remote host and sends the
+  initial SYN segment which opens the connection. 
+
+  The tcp_connect() function returns immediately; it does not wait for
+  the connection to be properly setup. Instead, it will call the
+  function specified as the fourth argument (the "connected" argument)
+  when the connection is established. If the connection could not be
+  properly established, either because the other host refused the
+  connection or because the other host didn't answer, the "err"
+  callback function of this pcb (registered with tcp_err, see below)
+  will be called.
+
+  The tcp_connect() function can return ERR_MEM if no memory is
+  available for enqueueing the SYN segment. If the SYN indeed was
+  enqueued successfully, the tcp_connect() function returns ERR_OK.
+
+
+--- Sending TCP data
+
+TCP data is sent by enqueueing the data with a call to
+tcp_write(). When the data is successfully transmitted to the remote
+host, the application will be notified with a call to a specified
+callback function.
+
+- err_t tcp_write(struct tcp_pcb *pcb, const void *dataptr, u16_t len,
+                  u8_t apiflags)
+
+  Enqueues the data pointed to by the argument dataptr. The length of
+  the data is passed as the len parameter. The apiflags can be one or more of:
+  - TCP_WRITE_FLAG_COPY: indicates whether the new memory should be allocated
+    for the data to be copied into. If this flag is not given, no new memory
+    should be allocated and the data should only be referenced by pointer. This
+    also means that the memory behind dataptr must not change until the data is
+    ACKed by the remote host
+  - TCP_WRITE_FLAG_MORE: indicates that more data follows. If this is given,
+    the PSH flag is set in the last segment created by this call to tcp_write.
+    If this flag is given, the PSH flag is not set.
+
+  The tcp_write() function will fail and return ERR_MEM if the length
+  of the data exceeds the current send buffer size or if the length of
+  the queue of outgoing segment is larger than the upper limit defined
+  in lwipopts.h. The number of bytes available in the output queue can
+  be retrieved with the tcp_sndbuf() function.
+
+  The proper way to use this function is to call the function with at
+  most tcp_sndbuf() bytes of data. If the function returns ERR_MEM,
+  the application should wait until some of the currently enqueued
+  data has been successfully received by the other host and try again.
+
+- void tcp_sent(struct tcp_pcb *pcb,
+                err_t (* sent)(void *arg, struct tcp_pcb *tpcb,
+                u16_t len))
+
+  Specifies the callback function that should be called when data has
+  successfully been received (i.e., acknowledged) by the remote
+  host. The len argument passed to the callback function gives the
+  amount bytes that was acknowledged by the last acknowledgment.
+
+  
+--- Receiving TCP data
+
+TCP data reception is callback based - an application specified
+callback function is called when new data arrives. When the
+application has taken the data, it has to call the tcp_recved()
+function to indicate that TCP can advertise increase the receive
+window.
+
+- void tcp_recv(struct tcp_pcb *pcb,
+                err_t (* recv)(void *arg, struct tcp_pcb *tpcb,
+                               struct pbuf *p, err_t err))
+
+  Sets the callback function that will be called when new data
+  arrives. The callback function will be passed a NULL pbuf to
+  indicate that the remote host has closed the connection. If
+  there are no errors and the callback function is to return
+  ERR_OK, then it must free the pbuf. Otherwise, it must not
+  free the pbuf so that lwIP core code can store it.
+
+- void tcp_recved(struct tcp_pcb *pcb, u16_t len)
+
+  Must be called when the application has received the data. The len
+  argument indicates the length of the received data.
+
+
+--- Application polling
+
+When a connection is idle (i.e., no data is either transmitted or
+received), lwIP will repeatedly poll the application by calling a
+specified callback function. This can be used either as a watchdog
+timer for killing connections that have stayed idle for too long, or
+as a method of waiting for memory to become available. For instance,
+if a call to tcp_write() has failed because memory wasn't available,
+the application may use the polling functionality to call tcp_write()
+again when the connection has been idle for a while.
+
+- void tcp_poll(struct tcp_pcb *pcb, 
+                err_t (* poll)(void *arg, struct tcp_pcb *tpcb),
+                u8_t interval)
+
+  Specifies the polling interval and the callback function that should
+  be called to poll the application. The interval is specified in
+  number of TCP coarse grained timer shots, which typically occurs
+  twice a second. An interval of 10 means that the application would
+  be polled every 5 seconds.
+
+
+--- Closing and aborting connections
+
+- err_t tcp_close(struct tcp_pcb *pcb)
+
+  Closes the connection. The function may return ERR_MEM if no memory
+  was available for closing the connection. If so, the application
+  should wait and try again either by using the acknowledgment
+  callback or the polling functionality. If the close succeeds, the
+  function returns ERR_OK.
+
+  The pcb is deallocated by the TCP code after a call to tcp_close(). 
+
+- void tcp_abort(struct tcp_pcb *pcb)
+
+  Aborts the connection by sending a RST (reset) segment to the remote
+  host. The pcb is deallocated. This function never fails.
+
+  ATTENTION: When calling this from one of the TCP callbacks, make
+  sure you always return ERR_ABRT (and never return ERR_ABRT otherwise
+  or you will risk accessing deallocated memory or memory leaks!
+
+
+If a connection is aborted because of an error, the application is
+alerted of this event by the err callback. Errors that might abort a
+connection are when there is a shortage of memory. The callback
+function to be called is set using the tcp_err() function.
+
+- void tcp_err(struct tcp_pcb *pcb, void (* err)(void *arg,
+       err_t err))
+
+  The error callback function does not get the pcb passed to it as a
+  parameter since the pcb may already have been deallocated.
+
+
+--- Lower layer TCP interface
+
+TCP provides a simple interface to the lower layers of the
+system. During system initialization, the function tcp_init() has
+to be called before any other TCP function is called. When the system
+is running, the two timer functions tcp_fasttmr() and tcp_slowtmr()
+must be called with regular intervals. The tcp_fasttmr() should be
+called every TCP_FAST_INTERVAL milliseconds (defined in tcp.h) and
+tcp_slowtmr() should be called every TCP_SLOW_INTERVAL milliseconds. 
+
+
+--- UDP interface
+
+The UDP interface is similar to that of TCP, but due to the lower
+level of complexity of UDP, the interface is significantly simpler.
+
+- struct udp_pcb *udp_new(void)
+
+  Creates a new UDP pcb which can be used for UDP communication. The
+  pcb is not active until it has either been bound to a local address
+  or connected to a remote address.
+
+- void udp_remove(struct udp_pcb *pcb)
+
+  Removes and deallocates the pcb.  
+  
+- err_t udp_bind(struct udp_pcb *pcb, ip_addr_t *ipaddr,
+                 u16_t port)
+
+  Binds the pcb to a local address. The IP-address argument "ipaddr"
+  can be IP_ADDR_ANY to indicate that it should listen to any local IP
+  address. The function currently always return ERR_OK.
+
+- err_t udp_connect(struct udp_pcb *pcb, ip_addr_t *ipaddr,
+                    u16_t port)
+
+  Sets the remote end of the pcb. This function does not generate any
+  network traffic, but only set the remote address of the pcb.
+
+- err_t udp_disconnect(struct udp_pcb *pcb)
+
+  Remove the remote end of the pcb. This function does not generate
+  any network traffic, but only removes the remote address of the pcb.
+
+- err_t udp_send(struct udp_pcb *pcb, struct pbuf *p)
+
+  Sends the pbuf p. The pbuf is not deallocated.
+
+- void udp_recv(struct udp_pcb *pcb,
+                void (* recv)(void *arg, struct udp_pcb *upcb,
+                                         struct pbuf *p,
+                                         ip_addr_t *addr,
+                                         u16_t port),
+                              void *recv_arg)
+
+  Specifies a callback function that should be called when a UDP
+  datagram is received.
+  
+
+--- System initalization
+
+A truly complete and generic sequence for initializing the lwip stack
+cannot be given because it depends on the build configuration (lwipopts.h)
+and additional initializations for your runtime environment (e.g. timers).
+
+We can give you some idea on how to proceed when using the raw API.
+We assume a configuration using a single Ethernet netif and the
+UDP and TCP transport layers, IPv4 and the DHCP client.
+
+Call these functions in the order of appearance:
+
+- stats_init()
+
+  Clears the structure where runtime statistics are gathered.
+
+- sys_init()
+  
+  Not of much use since we set the NO_SYS 1 option in lwipopts.h,
+  to be called for easy configuration changes.
+
+- mem_init()
+
+  Initializes the dynamic memory heap defined by MEM_SIZE.
+
+- memp_init()
+
+  Initializes the memory pools defined by MEMP_NUM_x.
+
+- pbuf_init()
+
+  Initializes the pbuf memory pool defined by PBUF_POOL_SIZE.
+  
+- etharp_init()
+
+  Initializes the ARP table and queue.
+  Note: you must call etharp_tmr at a ARP_TMR_INTERVAL (5 seconds) regular interval
+  after this initialization.
+
+- ip_init()
+
+  Doesn't do much, it should be called to handle future changes.
+
+- udp_init()
+
+  Clears the UDP PCB list.
+
+- tcp_init()
+
+  Clears the TCP PCB list and clears some internal TCP timers.
+  Note: you must call tcp_fasttmr() and tcp_slowtmr() at the
+  predefined regular intervals after this initialization. 
+  
+- netif_add(struct netif *netif, ip_addr_t *ipaddr,
+            ip_addr_t *netmask, ip_addr_t *gw,
+            void *state, err_t (* init)(struct netif *netif),
+            err_t (* input)(struct pbuf *p, struct netif *netif))
+
+  Adds your network interface to the netif_list. Allocate a struct
+  netif and pass a pointer to this structure as the first argument.
+  Give pointers to cleared ip_addr structures when using DHCP,
+  or fill them with sane numbers otherwise. The state pointer may be NULL.
+
+  The init function pointer must point to a initialization function for
+  your ethernet netif interface. The following code illustrates it's use.
+  
+  err_t netif_if_init(struct netif *netif)
+  {
+    u8_t i;
+    
+    for(i = 0; i < ETHARP_HWADDR_LEN; i++) netif->hwaddr[i] = some_eth_addr[i];
+    init_my_eth_device();
+    return ERR_OK;
+  }
+  
+  For ethernet drivers, the input function pointer must point to the lwip
+  function ethernet_input() declared in "netif/etharp.h". Other drivers
+  must use ip_input() declared in "lwip/ip.h".
+  
+- netif_set_default(struct netif *netif)
+
+  Registers the default network interface.
+
+- netif_set_up(struct netif *netif)
+
+  When the netif is fully configured this function must be called.
+
+- dhcp_start(struct netif *netif)
+
+  Creates a new DHCP client for this interface on the first call.
+  Note: you must call dhcp_fine_tmr() and dhcp_coarse_tmr() at
+  the predefined regular intervals after starting the client.
+  
+  You can peek in the netif->dhcp struct for the actual DHCP status.
+
+
+--- Optimalization hints
+
+The first thing you want to optimize is the lwip_standard_checksum()
+routine from src/core/inet.c. You can override this standard
+function with the #define LWIP_CHKSUM <your_checksum_routine>.
+
+There are C examples given in inet.c or you might want to
+craft an assembly function for this. RFC1071 is a good
+introduction to this subject.
+
+Other significant improvements can be made by supplying
+assembly or inline replacements for htons() and htonl()
+if you're using a little-endian architecture.
+#define LWIP_PLATFORM_BYTESWAP 1
+#define LWIP_PLATFORM_HTONS(x) <your_htons>
+#define LWIP_PLATFORM_HTONL(x) <your_htonl>
+
+Check your network interface driver if it reads at
+a higher speed than the maximum wire-speed. If the
+hardware isn't serviced frequently and fast enough
+buffer overflows are likely to occur.
+
+E.g. when using the cs8900 driver, call cs8900if_service(ethif)
+as frequently as possible. When using an RTOS let the cs8900 interrupt
+wake a high priority task that services your driver using a binary
+semaphore or event flag. Some drivers might allow additional tuning
+to match your application and network.
+
+For a production release it is recommended to set LWIP_STATS to 0.
+Note that speed performance isn't influenced much by simply setting
+high values to the memory options.
+
+For more optimization hints take a look at the lwIP wiki.
+
+--- Zero-copy MACs
+
+To achieve zero-copy on transmit, the data passed to the raw API must
+remain unchanged until sent. Because the send- (or write-)functions return
+when the packets have been enqueued for sending, data must be kept stable
+after that, too.
+
+This implies that PBUF_RAM/PBUF_POOL pbufs passed to raw-API send functions
+must *not* be reused by the application unless their ref-count is 1.
+
+For no-copy pbufs (PBUF_ROM/PBUF_REF), data must be kept unchanged, too,
+but the stack/driver will/must copy PBUF_REF'ed data when enqueueing, while
+PBUF_ROM-pbufs are just enqueued (as ROM-data is expected to never change).
+
+Also, data passed to tcp_write without the copy-flag must not be changed!
+
+Therefore, be careful which type of PBUF you use and if you copy TCP data
+or not!

+ 135 - 0
libs/thirdparty/LwIP/doc/savannah.txt

@@ -0,0 +1,135 @@
+Daily Use Guide for using Savannah for lwIP
+
+Table of Contents:
+
+1 - Obtaining lwIP from the CVS repository
+2 - Committers/developers CVS access using SSH (to be written)
+3 - Merging from DEVEL branch to main trunk (stable branch)
+4 - How to release lwIP
+
+
+
+1 Obtaining lwIP from the CVS repository
+----------------------------------------
+
+To perform an anonymous CVS checkout of the main trunk (this is where
+bug fixes and incremental enhancements occur), do this:
+
+cvs -z3 -d:pserver:anonymous@cvs.sv.gnu.org:/sources/lwip checkout lwip
+ 
+Or, obtain a stable branch (updated with bug fixes only) as follows:
+cvs -z3 -d:pserver:anonymous@cvs.sv.gnu.org:/sources/lwip checkout \
+  -r STABLE-0_7 -d lwip-0.7 lwip
+
+Or, obtain a specific (fixed) release as follows:
+cvs -z3 -d:pserver:anonymous@cvs.sv.gnu.org:/sources/lwip checkout \
+  -r STABLE-0_7_0 -d lwip-0.7.0 lwip
+
+3 Committers/developers CVS access using SSH
+--------------------------------------------
+
+The Savannah server uses SSH (Secure Shell) protocol 2 authentication and encryption.
+As such, CVS commits to the server occur through a SSH tunnel for project members.
+To create a SSH2 key pair in UNIX-like environments, do this:
+
+ssh-keygen -t dsa
+
+Under Windows, a recommended SSH client is "PuTTY", freely available with good
+documentation and a graphic user interface. Use its key generator.
+
+Now paste the id_dsa.pub contents into your Savannah account public key list. Wait
+a while so that Savannah can update its configuration (This can take minutes).
+
+Try to login using SSH:
+
+ssh -v your_login@cvs.sv.gnu.org
+
+If it tells you:
+
+Authenticating with public key "your_key_name"...
+Server refused to allocate pty
+
+then you could login; Savannah refuses to give you a shell - which is OK, as we
+are allowed to use SSH for CVS only. Now, you should be able to do this:
+
+export CVS_RSH=ssh
+cvs -z3 -d:ext:your_login@cvs.sv.gnu.org:/sources/lwip co lwip
+ 
+after which you can edit your local files with bug fixes or new features and
+commit them. Make sure you know what you are doing when using CVS to make
+changes on the repository. If in doubt, ask on the lwip-members mailing list.
+
+(If SSH asks about authenticity of the host, you can check the key
+ fingerprint against http://savannah.nongnu.org/cvs/?group=lwip)
+
+
+3 Merging from DEVEL branch to main trunk (stable)
+--------------------------------------------------
+
+Merging is a delicate process in CVS and requires the
+following disciplined steps in order to prevent conflicts
+in the future. Conflicts can be hard to solve!
+
+Merging from branch A to branch B requires that the A branch
+has a tag indicating the previous merger. This tag is called
+'merged_from_A_to_B'. After merging, the tag is moved in the
+A branch to remember this merger for future merge actions.
+
+IMPORTANT: AFTER COMMITTING A SUCCESFUL MERGE IN THE
+REPOSITORY, THE TAG MUST BE SET ON THE SOURCE BRANCH OF THE
+MERGE ACTION (REPLACING EXISTING TAGS WITH THE SAME NAME).
+
+Merge all changes in DEVEL since our last merge to main:
+
+In the working copy of the main trunk:
+cvs update -P -jmerged_from_DEVEL_to_main -jDEVEL 
+
+(This will apply the changes between 'merged_from_DEVEL_to_main'
+and 'DEVEL' to your work set of files)
+
+We can now commit the merge result.
+cvs commit -R -m "Merged from DEVEL to main." 
+
+If this worked out OK, we now move the tag in the DEVEL branch
+to this merge point, so we can use this point for future merges:
+
+cvs rtag -F -r DEVEL merged_from_DEVEL_to_main lwip 
+
+4 How to release lwIP
+---------------------
+
+First, checkout a clean copy of the branch to be released. Tag this set with
+tag name "STABLE-0_6_3". (I use release number 0.6.3 throughout this example).
+
+Login CVS using pserver authentication, then export a clean copy of the
+tagged tree. Export is similar to a checkout, except that the CVS metadata
+is not created locally. 
+
+export CVS_RSH=ssh
+cvs -z3 -d:pserver:anonymous@cvs.sv.gnu.org:/sources/lwip checkout \
+  -r STABLE-0_6_3 -d lwip-0.6.3 lwip
+
+Archive this directory using tar, gzip'd, bzip2'd and zip'd.
+
+tar czvf lwip-0.6.3.tar.gz lwip-0.6.3
+tar cjvf lwip-0.6.3.tar.bz2 lwip-0.6.3
+zip -r lwip-0.6.3.zip lwip-0.6.3
+
+Now, sign the archives with a detached GPG binary signature as follows:
+
+gpg -b lwip-0.6.3.tar.gz
+gpg -b lwip-0.6.3.tar.bz2
+gpg -b lwip-0.6.3.zip
+
+Upload these files using anonymous FTP:
+ncftp ftp://savannah.gnu.org/incoming/savannah/lwip
+
+ncftp>mput *0.6.3.*
+
+Additionally, you may post a news item on Savannah, like this:
+
+A new 0.6.3 release is now available here:
+http://savannah.nongnu.org/files/?group=lwip&highlight=0.6.3
+
+You will have to submit this via the user News interface, then approve
+this via the Administrator News interface.

+ 181 - 0
libs/thirdparty/LwIP/doc/snmp_agent.txt

@@ -0,0 +1,181 @@
+SNMPv1 agent for lwIP
+
+Author: Christiaan Simons
+
+This is a brief introduction how to use and configure the SNMP agent.
+Note the agent uses the raw-API UDP interface so you may also want to
+read rawapi.txt to gain a better understanding of the SNMP message handling.
+
+0 Agent Capabilities
+====================
+
+SNMPv1 per RFC1157
+  This is an old(er) standard but is still widely supported.
+  For SNMPv2c and v3 have a greater complexity and need many
+  more lines of code. IMHO this breaks the idea of "lightweight IP".
+
+  Note the S in SNMP stands for "Simple". Note that "Simple" is
+  relative. SNMP is simple compared to the complex ISO network
+  management protocols CMIP (Common Management Information Protocol)
+  and CMOT (CMip Over Tcp).
+
+MIB II per RFC1213
+  The standard lwIP stack management information base.
+  This is a required MIB, so this is always enabled.
+  When builing lwIP without TCP, the mib-2.tcp group is omitted.
+  The groups EGP, CMOT and transmission are disabled by default.
+  
+  Most mib-2 objects are not writable except:
+  sysName, sysLocation, sysContact, snmpEnableAuthenTraps.
+  Writing to or changing the ARP and IP address and route
+  tables is not possible.
+ 
+  Note lwIP has a very limited notion of IP routing. It currently
+  doen't have a route table and doesn't have a notion of the U,G,H flags.
+  Instead lwIP uses the interface list with only one default interface
+  acting as a single gateway interface (G) for the default route.
+
+  The agent returns a "virtual table" with the default route 0.0.0.0
+  for the default interface and network routes (no H) for each
+  network interface in the netif_list.
+  All routes are considered to be up (U).
+
+Loading additional MIBs
+  MIBs can only be added in compile-time, not in run-time.
+  There is no MIB compiler thus additional MIBs must be hand coded.
+
+Large SNMP message support
+  The packet decoding and encoding routines are designed
+  to use pbuf-chains. Larger payloads than the minimum
+  SNMP requirement of 484 octets are supported if the 
+  PBUF_POOL_SIZE and IP_REASS_BUFSIZE are set to match your
+  local requirement.
+
+1 Building the Agent
+====================
+
+First of all you'll need to add the following define
+to your local lwipopts.h:
+
+#define LWIP_SNMP               1
+
+and add the source files in lwip/src/core/snmp
+and some snmp headers in lwip/src/include/lwip to your makefile.
+
+Note you'll might need to adapt you network driver to update
+the mib2 variables for your interface.
+
+2 Running the Agent
+===================
+
+The following function calls must be made in your program to
+actually get the SNMP agent running.
+
+Before starting the agent you should supply pointers
+to non-volatile memory for sysContact, sysLocation,
+and snmpEnableAuthenTraps. You can do this by calling
+
+snmp_set_syscontact()
+snmp_set_syslocation()
+snmp_set_snmpenableauthentraps()
+
+Additionally you may want to set
+
+snmp_set_sysdescr()
+snmp_set_sysobjid() (if you have a private MIB)
+snmp_set_sysname()
+
+Also before starting the agent you need to setup
+one or more trap destinations using these calls:
+
+snmp_trap_dst_enable();
+snmp_trap_dst_ip_set();
+
+In the lwIP initialisation sequence call snmp_init() just after
+the call to udp_init().
+
+Exactly every 10 msec the SNMP uptime timestamp must be updated with
+snmp_inc_sysuptime(). You should call this from a timer interrupt
+or a timer signal handler depending on your runtime environment.
+
+An alternative way to update the SNMP uptime timestamp is to do a call like
+snmp_add_sysuptime(100) each 1000ms (which is bigger "step", but call to
+a lower frequency). Another one is to not call snmp_inc_sysuptime() or
+snmp_add_sysuptime(), and to define the SNMP_GET_SYSUPTIME(sysuptime) macro.
+This one is undefined by default in mib2.c. SNMP_GET_SYSUPTIME is called inside
+snmp_get_sysuptime(u32_t *value), and enable to change "sysuptime" value only
+when it's queried (any function which need "sysuptime" have to call
+snmp_get_sysuptime).
+
+
+3 Private MIBs
+==============
+
+If want to extend the agent with your own private MIB you'll need to
+add the following define to your local lwipopts.h:
+
+#define SNMP_PRIVATE_MIB        1
+
+You must provide the private_mib.h and associated files yourself.
+Note we don't have a "MIB compiler" that generates C source from a MIB,
+so you're required to do some serious coding if you enable this!
+
+Note the lwIP enterprise ID (26381) is assigned to the lwIP project,
+ALL OBJECT IDENTIFIERS LIVING UNDER THIS ID ARE ASSIGNED BY THE lwIP
+MAINTAINERS!
+
+If you need to create your own private MIB you'll need
+to apply for your own enterprise ID with IANA: http://www.iana.org/numbers.html 
+
+You can set it by passing a struct snmp_obj_id to the agent
+using snmp_set_sysobjid(&my_object_id), just before snmp_init().
+
+Note the object identifiers for thes MIB-2 and your private MIB
+tree must be kept in sorted ascending (lexicographical) order.
+This to ensure correct getnext operation.
+
+An example for a private MIB is part of the "minimal Unix" project:
+contrib/ports/unix/proj/minimal/lwip_prvmib.c
+
+The next chapter gives a more detailed description of the
+MIB-2 tree and the optional private MIB.
+
+4 The Gory Details
+==================
+
+4.0 Object identifiers and the MIB tree.
+
+We have three distinct parts for all object identifiers:
+
+The prefix
+  .iso.org.dod.internet
+
+the middle part 
+  .mgmt.mib-2.ip.ipNetToMediaTable.ipNetToMediaEntry.ipNetToMediaPhysAddress
+
+and the index part
+  .1.192.168.0.1
+
+Objects located above the .internet hierarchy aren't supported.
+Currently only the .mgmt sub-tree is available and
+when the SNMP_PRIVATE_MIB is enabled the .private tree
+becomes available too.
+
+Object identifiers from incoming requests are checked
+for a matching prefix, middle part and index part
+or are expanded(*) for GetNext requests with short
+or inexisting names in the request.
+(* we call this "expansion" but this also
+resembles the "auto-completion" operation)
+
+The middle part is usually located in ROM (const)
+to preserve precious RAM on small microcontrollers.
+However RAM location is possible for a dynamically
+changing private tree.
+
+The index part is handled by functions which in
+turn use dynamically allocated index trees from RAM.
+These trees are updated by e.g. the etharp code
+when new entries are made or removed form the ARP cache.
+
+/** @todo more gory details */

+ 267 - 0
libs/thirdparty/LwIP/doc/sys_arch.txt

@@ -0,0 +1,267 @@
+sys_arch interface for lwIP 0.6++
+
+Author: Adam Dunkels
+
+The operating system emulation layer provides a common interface
+between the lwIP code and the underlying operating system kernel. The
+general idea is that porting lwIP to new architectures requires only
+small changes to a few header files and a new sys_arch
+implementation. It is also possible to do a sys_arch implementation
+that does not rely on any underlying operating system.
+
+The sys_arch provides semaphores and mailboxes to lwIP. For the full
+lwIP functionality, multiple threads support can be implemented in the
+sys_arch, but this is not required for the basic lwIP
+functionality. Previous versions of lwIP required the sys_arch to
+implement timer scheduling as well but as of lwIP 0.5 this is
+implemented in a higher layer.
+
+In addition to the source file providing the functionality of sys_arch,
+the OS emulation layer must provide several header files defining
+macros used throughout lwip.  The files required and the macros they
+must define are listed below the sys_arch description.
+
+Semaphores can be either counting or binary - lwIP works with both
+kinds. Mailboxes are used for message passing and can be implemented
+either as a queue which allows multiple messages to be posted to a
+mailbox, or as a rendez-vous point where only one message can be
+posted at a time. lwIP works with both kinds, but the former type will
+be more efficient. A message in a mailbox is just a pointer, nothing
+more. 
+
+Semaphores are represented by the type "sys_sem_t" which is typedef'd
+in the sys_arch.h file. Mailboxes are equivalently represented by the
+type "sys_mbox_t". lwIP does not place any restrictions on how
+sys_sem_t or sys_mbox_t are represented internally.
+
+Since lwIP 1.4.0, semaphore and mailbox functions are prototyped in a way that
+allows both using pointers or actual OS structures to be used. This way, memory
+required for such types can be either allocated in place (globally or on the
+stack) or on the heap (allocated internally in the "*_new()" functions).
+
+The following functions must be implemented by the sys_arch:
+
+- void sys_init(void)
+
+  Is called to initialize the sys_arch layer.
+
+- err_t sys_sem_new(sys_sem_t *sem, u8_t count)
+
+  Creates a new semaphore. The semaphore is allocated to the memory that 'sem'
+  points to (which can be both a pointer or the actual OS structure).
+  The "count" argument specifies the initial state of the semaphore (which is
+  either 0 or 1).
+  If the semaphore has been created, ERR_OK should be returned. Returning any
+  other error will provide a hint what went wrong, but except for assertions,
+  no real error handling is implemented.
+
+- void sys_sem_free(sys_sem_t *sem)
+
+  Deallocates a semaphore.
+
+- void sys_sem_signal(sys_sem_t *sem)
+
+  Signals a semaphore.
+
+- u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout)
+
+  Blocks the thread while waiting for the semaphore to be
+  signaled. If the "timeout" argument is non-zero, the thread should
+  only be blocked for the specified time (measured in
+  milliseconds). If the "timeout" argument is zero, the thread should be
+  blocked until the semaphore is signalled.
+
+  If the timeout argument is non-zero, the return value is the number of
+  milliseconds spent waiting for the semaphore to be signaled. If the
+  semaphore wasn't signaled within the specified time, the return value is
+  SYS_ARCH_TIMEOUT. If the thread didn't have to wait for the semaphore
+  (i.e., it was already signaled), the function may return zero.
+
+  Notice that lwIP implements a function with a similar name,
+  sys_sem_wait(), that uses the sys_arch_sem_wait() function.
+
+- int sys_sem_valid(sys_sem_t *sem)
+
+  Returns 1 if the semaphore is valid, 0 if it is not valid.
+  When using pointers, a simple way is to check the pointer for != NULL.
+  When directly using OS structures, implementing this may be more complex.
+  This may also be a define, in which case the function is not prototyped.
+
+- void sys_sem_set_invalid(sys_sem_t *sem)
+
+  Invalidate a semaphore so that sys_sem_valid() returns 0.
+  ATTENTION: This does NOT mean that the semaphore shall be deallocated:
+  sys_sem_free() is always called before calling this function!
+  This may also be a define, in which case the function is not prototyped.
+
+- err_t sys_mbox_new(sys_mbox_t *mbox, int size)
+
+  Creates an empty mailbox for maximum "size" elements. Elements stored
+  in mailboxes are pointers. You have to define macros "_MBOX_SIZE"
+  in your lwipopts.h, or ignore this parameter in your implementation
+  and use a default size.
+  If the mailbox has been created, ERR_OK should be returned. Returning any
+  other error will provide a hint what went wrong, but except for assertions,
+  no real error handling is implemented.
+
+- void sys_mbox_free(sys_mbox_t *mbox)
+
+  Deallocates a mailbox. If there are messages still present in the
+  mailbox when the mailbox is deallocated, it is an indication of a
+  programming error in lwIP and the developer should be notified.
+
+- void sys_mbox_post(sys_mbox_t *mbox, void *msg)
+
+  Posts the "msg" to the mailbox. This function have to block until
+  the "msg" is really posted.
+
+- err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg)
+
+  Try to post the "msg" to the mailbox. Returns ERR_MEM if this one
+  is full, else, ERR_OK if the "msg" is posted.
+
+- u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout)
+
+  Blocks the thread until a message arrives in the mailbox, but does
+  not block the thread longer than "timeout" milliseconds (similar to
+  the sys_arch_sem_wait() function). If "timeout" is 0, the thread should
+  be blocked until a message arrives. The "msg" argument is a result
+  parameter that is set by the function (i.e., by doing "*msg =
+  ptr"). The "msg" parameter maybe NULL to indicate that the message
+  should be dropped.
+
+  The return values are the same as for the sys_arch_sem_wait() function:
+  Number of milliseconds spent waiting or SYS_ARCH_TIMEOUT if there was a
+  timeout.
+
+  Note that a function with a similar name, sys_mbox_fetch(), is
+  implemented by lwIP. 
+
+- u32_t sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg)
+
+  This is similar to sys_arch_mbox_fetch, however if a message is not
+  present in the mailbox, it immediately returns with the code
+  SYS_MBOX_EMPTY. On success 0 is returned.
+
+  To allow for efficient implementations, this can be defined as a
+  function-like macro in sys_arch.h instead of a normal function. For
+  example, a naive implementation could be:
+    #define sys_arch_mbox_tryfetch(mbox,msg) \
+      sys_arch_mbox_fetch(mbox,msg,1)
+  although this would introduce unnecessary delays.
+
+- int sys_mbox_valid(sys_mbox_t *mbox)
+
+  Returns 1 if the mailbox is valid, 0 if it is not valid.
+  When using pointers, a simple way is to check the pointer for != NULL.
+  When directly using OS structures, implementing this may be more complex.
+  This may also be a define, in which case the function is not prototyped.
+
+- void sys_mbox_set_invalid(sys_mbox_t *mbox)
+
+  Invalidate a mailbox so that sys_mbox_valid() returns 0.
+  ATTENTION: This does NOT mean that the mailbox shall be deallocated:
+  sys_mbox_free() is always called before calling this function!
+  This may also be a define, in which case the function is not prototyped.
+
+If threads are supported by the underlying operating system and if
+such functionality is needed in lwIP, the following function will have
+to be implemented as well:
+
+- sys_thread_t sys_thread_new(char *name, void (* thread)(void *arg), void *arg, int stacksize, int prio)
+
+  Starts a new thread named "name" with priority "prio" that will begin its
+  execution in the function "thread()". The "arg" argument will be passed as an
+  argument to the thread() function. The stack size to used for this thread is
+  the "stacksize" parameter. The id of the new thread is returned. Both the id
+  and the priority are system dependent.
+
+- sys_prot_t sys_arch_protect(void)
+
+  This optional function does a "fast" critical region protection and returns
+  the previous protection level. This function is only called during very short
+  critical regions. An embedded system which supports ISR-based drivers might
+  want to implement this function by disabling interrupts. Task-based systems
+  might want to implement this by using a mutex or disabling tasking. This
+  function should support recursive calls from the same task or interrupt. In
+  other words, sys_arch_protect() could be called while already protected. In
+  that case the return value indicates that it is already protected.
+
+  sys_arch_protect() is only required if your port is supporting an operating
+  system.
+
+- void sys_arch_unprotect(sys_prot_t pval)
+
+  This optional function does a "fast" set of critical region protection to the
+  value specified by pval. See the documentation for sys_arch_protect() for
+  more information. This function is only required if your port is supporting
+  an operating system.
+
+For some configurations, you also need:
+
+- u32_t sys_now(void)
+
+  This optional function returns the current time in milliseconds (don't care
+  for wraparound, this is only used for time diffs).
+  Not implementing this function means you cannot use some modules (e.g. TCP
+  timestamps, internal timeouts for NO_SYS==1).
+
+
+Note:
+
+Be carefull with using mem_malloc() in sys_arch. When malloc() refers to
+mem_malloc() you can run into a circular function call problem. In mem.c
+mem_init() tries to allcate a semaphore using mem_malloc, which of course
+can't be performed when sys_arch uses mem_malloc.
+
+-------------------------------------------------------------------------------
+Additional files required for the "OS support" emulation layer:
+-------------------------------------------------------------------------------
+
+cc.h       - Architecture environment, some compiler specific, some
+             environment specific (probably should move env stuff 
+             to sys_arch.h.)
+
+  Typedefs for the types used by lwip -
+    u8_t, s8_t, u16_t, s16_t, u32_t, s32_t, mem_ptr_t
+
+  Compiler hints for packing lwip's structures -
+    PACK_STRUCT_FIELD(x)
+    PACK_STRUCT_STRUCT
+    PACK_STRUCT_BEGIN
+    PACK_STRUCT_END
+
+  Platform specific diagnostic output -
+    LWIP_PLATFORM_DIAG(x)    - non-fatal, print a message.
+    LWIP_PLATFORM_ASSERT(x)  - fatal, print message and abandon execution.
+    Portability defines for printf formatters:
+    U16_F, S16_F, X16_F, U32_F, S32_F, X32_F, SZT_F
+
+  "lightweight" synchronization mechanisms -
+    SYS_ARCH_DECL_PROTECT(x) - declare a protection state variable.
+    SYS_ARCH_PROTECT(x)      - enter protection mode.
+    SYS_ARCH_UNPROTECT(x)    - leave protection mode.
+
+  If the compiler does not provide memset() this file must include a
+  definition of it, or include a file which defines it.
+
+  This file must either include a system-local <errno.h> which defines
+  the standard *nix error codes, or it should #define LWIP_PROVIDE_ERRNO
+  to make lwip/arch.h define the codes which are used throughout.
+
+
+perf.h     - Architecture specific performance measurement.
+  Measurement calls made throughout lwip, these can be defined to nothing.
+    PERF_START               - start measuring something.
+    PERF_STOP(x)             - stop measuring something, and record the result.
+
+sys_arch.h - Tied to sys_arch.c
+
+  Arch dependent types for the following objects:
+    sys_sem_t, sys_mbox_t, sys_thread_t,
+  And, optionally:
+    sys_prot_t
+
+  Defines to set vars of sys_mbox_t and sys_sem_t to NULL.
+    SYS_MBOX_NULL NULL
+    SYS_SEM_NULL NULL

+ 135 - 0
libs/thirdparty/LwIP/doc90savannah.txt

@@ -0,0 +1,135 @@
+Daily Use Guide for using Savannah for lwIP
+
+Table of Contents:
+
+1 - Obtaining lwIP from the CVS repository
+2 - Committers/developers CVS access using SSH (to be written)
+3 - Merging from DEVEL branch to main trunk (stable branch)
+4 - How to release lwIP
+
+
+
+1 Obtaining lwIP from the CVS repository
+----------------------------------------
+
+To perform an anonymous CVS checkout of the main trunk (this is where
+bug fixes and incremental enhancements occur), do this:
+
+cvs -z3 -d:pserver:anonymous@cvs.sv.gnu.org:/sources/lwip checkout lwip
+ 
+Or, obtain a stable branch (updated with bug fixes only) as follows:
+cvs -z3 -d:pserver:anonymous@cvs.sv.gnu.org:/sources/lwip checkout \
+  -r STABLE-0_7 -d lwip-0.7 lwip
+
+Or, obtain a specific (fixed) release as follows:
+cvs -z3 -d:pserver:anonymous@cvs.sv.gnu.org:/sources/lwip checkout \
+  -r STABLE-0_7_0 -d lwip-0.7.0 lwip
+
+3 Committers/developers CVS access using SSH
+--------------------------------------------
+
+The Savannah server uses SSH (Secure Shell) protocol 2 authentication and encryption.
+As such, CVS commits to the server occur through a SSH tunnel for project members.
+To create a SSH2 key pair in UNIX-like environments, do this:
+
+ssh-keygen -t dsa
+
+Under Windows, a recommended SSH client is "PuTTY", freely available with good
+documentation and a graphic user interface. Use its key generator.
+
+Now paste the id_dsa.pub contents into your Savannah account public key list. Wait
+a while so that Savannah can update its configuration (This can take minutes).
+
+Try to login using SSH:
+
+ssh -v your_login@cvs.sv.gnu.org
+
+If it tells you:
+
+Authenticating with public key "your_key_name"...
+Server refused to allocate pty
+
+then you could login; Savannah refuses to give you a shell - which is OK, as we
+are allowed to use SSH for CVS only. Now, you should be able to do this:
+
+export CVS_RSH=ssh
+cvs -z3 -d:ext:your_login@cvs.sv.gnu.org:/sources/lwip co lwip
+ 
+after which you can edit your local files with bug fixes or new features and
+commit them. Make sure you know what you are doing when using CVS to make
+changes on the repository. If in doubt, ask on the lwip-members mailing list.
+
+(If SSH asks about authenticity of the host, you can check the key
+ fingerprint against http://savannah.nongnu.org/cvs/?group=lwip)
+
+
+3 Merging from DEVEL branch to main trunk (stable)
+--------------------------------------------------
+
+Merging is a delicate process in CVS and requires the
+following disciplined steps in order to prevent conflicts
+in the future. Conflicts can be hard to solve!
+
+Merging from branch A to branch B requires that the A branch
+has a tag indicating the previous merger. This tag is called
+'merged_from_A_to_B'. After merging, the tag is moved in the
+A branch to remember this merger for future merge actions.
+
+IMPORTANT: AFTER COMMITTING A SUCCESFUL MERGE IN THE
+REPOSITORY, THE TAG MUST BE SET ON THE SOURCE BRANCH OF THE
+MERGE ACTION (REPLACING EXISTING TAGS WITH THE SAME NAME).
+
+Merge all changes in DEVEL since our last merge to main:
+
+In the working copy of the main trunk:
+cvs update -P -jmerged_from_DEVEL_to_main -jDEVEL 
+
+(This will apply the changes between 'merged_from_DEVEL_to_main'
+and 'DEVEL' to your work set of files)
+
+We can now commit the merge result.
+cvs commit -R -m "Merged from DEVEL to main." 
+
+If this worked out OK, we now move the tag in the DEVEL branch
+to this merge point, so we can use this point for future merges:
+
+cvs rtag -F -r DEVEL merged_from_DEVEL_to_main lwip 
+
+4 How to release lwIP
+---------------------
+
+First, checkout a clean copy of the branch to be released. Tag this set with
+tag name "STABLE-0_6_3". (I use release number 0.6.3 throughout this example).
+
+Login CVS using pserver authentication, then export a clean copy of the
+tagged tree. Export is similar to a checkout, except that the CVS metadata
+is not created locally. 
+
+export CVS_RSH=ssh
+cvs -z3 -d:pserver:anonymous@cvs.sv.gnu.org:/sources/lwip checkout \
+  -r STABLE-0_6_3 -d lwip-0.6.3 lwip
+
+Archive this directory using tar, gzip'd, bzip2'd and zip'd.
+
+tar czvf lwip-0.6.3.tar.gz lwip-0.6.3
+tar cjvf lwip-0.6.3.tar.bz2 lwip-0.6.3
+zip -r lwip-0.6.3.zip lwip-0.6.3
+
+Now, sign the archives with a detached GPG binary signature as follows:
+
+gpg -b lwip-0.6.3.tar.gz
+gpg -b lwip-0.6.3.tar.bz2
+gpg -b lwip-0.6.3.zip
+
+Upload these files using anonymous FTP:
+ncftp ftp://savannah.gnu.org/incoming/savannah/lwip
+
+ncftp>mput *0.6.3.*
+
+Additionally, you may post a news item on Savannah, like this:
+
+A new 0.6.3 release is now available here:
+http://savannah.nongnu.org/files/?group=lwip&highlight=0.6.3
+
+You will have to submit this via the user News interface, then approve
+this via the Administrator News interface.

+ 267 - 0
libs/thirdparty/LwIP/docpsys_arch.txt

@@ -0,0 +1,267 @@
+sys_arch interface for lwIP 0.6++
+
+Author: Adam Dunkels
+
+The operating system emulation layer provides a common interface
+between the lwIP code and the underlying operating system kernel. The
+general idea is that porting lwIP to new architectures requires only
+small changes to a few header files and a new sys_arch
+implementation. It is also possible to do a sys_arch implementation
+that does not rely on any underlying operating system.
+
+The sys_arch provides semaphores and mailboxes to lwIP. For the full
+lwIP functionality, multiple threads support can be implemented in the
+sys_arch, but this is not required for the basic lwIP
+functionality. Previous versions of lwIP required the sys_arch to
+implement timer scheduling as well but as of lwIP 0.5 this is
+implemented in a higher layer.
+
+In addition to the source file providing the functionality of sys_arch,
+the OS emulation layer must provide several header files defining
+macros used throughout lwip.  The files required and the macros they
+must define are listed below the sys_arch description.
+
+Semaphores can be either counting or binary - lwIP works with both
+kinds. Mailboxes are used for message passing and can be implemented
+either as a queue which allows multiple messages to be posted to a
+mailbox, or as a rendez-vous point where only one message can be
+posted at a time. lwIP works with both kinds, but the former type will
+be more efficient. A message in a mailbox is just a pointer, nothing
+more. 
+
+Semaphores are represented by the type "sys_sem_t" which is typedef'd
+in the sys_arch.h file. Mailboxes are equivalently represented by the
+type "sys_mbox_t". lwIP does not place any restrictions on how
+sys_sem_t or sys_mbox_t are represented internally.
+
+Since lwIP 1.4.0, semaphore and mailbox functions are prototyped in a way that
+allows both using pointers or actual OS structures to be used. This way, memory
+required for such types can be either allocated in place (globally or on the
+stack) or on the heap (allocated internally in the "*_new()" functions).
+
+The following functions must be implemented by the sys_arch:
+
+- void sys_init(void)
+
+  Is called to initialize the sys_arch layer.
+
+- err_t sys_sem_new(sys_sem_t *sem, u8_t count)
+
+  Creates a new semaphore. The semaphore is allocated to the memory that 'sem'
+  points to (which can be both a pointer or the actual OS structure).
+  The "count" argument specifies the initial state of the semaphore (which is
+  either 0 or 1).
+  If the semaphore has been created, ERR_OK should be returned. Returning any
+  other error will provide a hint what went wrong, but except for assertions,
+  no real error handling is implemented.
+
+- void sys_sem_free(sys_sem_t *sem)
+
+  Deallocates a semaphore.
+
+- void sys_sem_signal(sys_sem_t *sem)
+
+  Signals a semaphore.
+
+- u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout)
+
+  Blocks the thread while waiting for the semaphore to be
+  signaled. If the "timeout" argument is non-zero, the thread should
+  only be blocked for the specified time (measured in
+  milliseconds). If the "timeout" argument is zero, the thread should be
+  blocked until the semaphore is signalled.
+
+  If the timeout argument is non-zero, the return value is the number of
+  milliseconds spent waiting for the semaphore to be signaled. If the
+  semaphore wasn't signaled within the specified time, the return value is
+  SYS_ARCH_TIMEOUT. If the thread didn't have to wait for the semaphore
+  (i.e., it was already signaled), the function may return zero.
+
+  Notice that lwIP implements a function with a similar name,
+  sys_sem_wait(), that uses the sys_arch_sem_wait() function.
+
+- int sys_sem_valid(sys_sem_t *sem)
+
+  Returns 1 if the semaphore is valid, 0 if it is not valid.
+  When using pointers, a simple way is to check the pointer for != NULL.
+  When directly using OS structures, implementing this may be more complex.
+  This may also be a define, in which case the function is not prototyped.
+
+- void sys_sem_set_invalid(sys_sem_t *sem)
+
+  Invalidate a semaphore so that sys_sem_valid() returns 0.
+  ATTENTION: This does NOT mean that the semaphore shall be deallocated:
+  sys_sem_free() is always called before calling this function!
+  This may also be a define, in which case the function is not prototyped.
+
+- err_t sys_mbox_new(sys_mbox_t *mbox, int size)
+
+  Creates an empty mailbox for maximum "size" elements. Elements stored
+  in mailboxes are pointers. You have to define macros "_MBOX_SIZE"
+  in your lwipopts.h, or ignore this parameter in your implementation
+  and use a default size.
+  If the mailbox has been created, ERR_OK should be returned. Returning any
+  other error will provide a hint what went wrong, but except for assertions,
+  no real error handling is implemented.
+
+- void sys_mbox_free(sys_mbox_t *mbox)
+
+  Deallocates a mailbox. If there are messages still present in the
+  mailbox when the mailbox is deallocated, it is an indication of a
+  programming error in lwIP and the developer should be notified.
+
+- void sys_mbox_post(sys_mbox_t *mbox, void *msg)
+
+  Posts the "msg" to the mailbox. This function have to block until
+  the "msg" is really posted.
+
+- err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg)
+
+  Try to post the "msg" to the mailbox. Returns ERR_MEM if this one
+  is full, else, ERR_OK if the "msg" is posted.
+
+- u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout)
+
+  Blocks the thread until a message arrives in the mailbox, but does
+  not block the thread longer than "timeout" milliseconds (similar to
+  the sys_arch_sem_wait() function). If "timeout" is 0, the thread should
+  be blocked until a message arrives. The "msg" argument is a result
+  parameter that is set by the function (i.e., by doing "*msg =
+  ptr"). The "msg" parameter maybe NULL to indicate that the message
+  should be dropped.
+
+  The return values are the same as for the sys_arch_sem_wait() function:
+  Number of milliseconds spent waiting or SYS_ARCH_TIMEOUT if there was a
+  timeout.
+
+  Note that a function with a similar name, sys_mbox_fetch(), is
+  implemented by lwIP. 
+
+- u32_t sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg)
+
+  This is similar to sys_arch_mbox_fetch, however if a message is not
+  present in the mailbox, it immediately returns with the code
+  SYS_MBOX_EMPTY. On success 0 is returned.
+
+  To allow for efficient implementations, this can be defined as a
+  function-like macro in sys_arch.h instead of a normal function. For
+  example, a naive implementation could be:
+    #define sys_arch_mbox_tryfetch(mbox,msg) \
+      sys_arch_mbox_fetch(mbox,msg,1)
+  although this would introduce unnecessary delays.
+
+- int sys_mbox_valid(sys_mbox_t *mbox)
+
+  Returns 1 if the mailbox is valid, 0 if it is not valid.
+  When using pointers, a simple way is to check the pointer for != NULL.
+  When directly using OS structures, implementing this may be more complex.
+  This may also be a define, in which case the function is not prototyped.
+
+- void sys_mbox_set_invalid(sys_mbox_t *mbox)
+
+  Invalidate a mailbox so that sys_mbox_valid() returns 0.
+  ATTENTION: This does NOT mean that the mailbox shall be deallocated:
+  sys_mbox_free() is always called before calling this function!
+  This may also be a define, in which case the function is not prototyped.
+
+If threads are supported by the underlying operating system and if
+such functionality is needed in lwIP, the following function will have
+to be implemented as well:
+
+- sys_thread_t sys_thread_new(char *name, void (* thread)(void *arg), void *arg, int stacksize, int prio)
+
+  Starts a new thread named "name" with priority "prio" that will begin its
+  execution in the function "thread()". The "arg" argument will be passed as an
+  argument to the thread() function. The stack size to used for this thread is
+  the "stacksize" parameter. The id of the new thread is returned. Both the id
+  and the priority are system dependent.
+
+- sys_prot_t sys_arch_protect(void)
+
+  This optional function does a "fast" critical region protection and returns
+  the previous protection level. This function is only called during very short
+  critical regions. An embedded system which supports ISR-based drivers might
+  want to implement this function by disabling interrupts. Task-based systems
+  might want to implement this by using a mutex or disabling tasking. This
+  function should support recursive calls from the same task or interrupt. In
+  other words, sys_arch_protect() could be called while already protected. In
+  that case the return value indicates that it is already protected.
+
+  sys_arch_protect() is only required if your port is supporting an operating
+  system.
+
+- void sys_arch_unprotect(sys_prot_t pval)
+
+  This optional function does a "fast" set of critical region protection to the
+  value specified by pval. See the documentation for sys_arch_protect() for
+  more information. This function is only required if your port is supporting
+  an operating system.
+
+For some configurations, you also need:
+
+- u32_t sys_now(void)
+
+  This optional function returns the current time in milliseconds (don't care
+  for wraparound, this is only used for time diffs).
+  Not implementing this function means you cannot use some modules (e.g. TCP
+  timestamps, internal timeouts for NO_SYS==1).
+
+
+Note:
+
+Be carefull with using mem_malloc() in sys_arch. When malloc() refers to
+mem_malloc() you can run into a circular function call problem. In mem.c
+mem_init() tries to allcate a semaphore using mem_malloc, which of course
+can't be performed when sys_arch uses mem_malloc.
+
+-------------------------------------------------------------------------------
+Additional files required for the "OS support" emulation layer:
+-------------------------------------------------------------------------------
+
+cc.h       - Architecture environment, some compiler specific, some
+             environment specific (probably should move env stuff 
+             to sys_arch.h.)
+
+  Typedefs for the types used by lwip -
+    u8_t, s8_t, u16_t, s16_t, u32_t, s32_t, mem_ptr_t
+
+  Compiler hints for packing lwip's structures -
+    PACK_STRUCT_FIELD(x)
+    PACK_STRUCT_STRUCT
+    PACK_STRUCT_BEGIN
+    PACK_STRUCT_END
+
+  Platform specific diagnostic output -
+    LWIP_PLATFORM_DIAG(x)    - non-fatal, print a message.
+    LWIP_PLATFORM_ASSERT(x)  - fatal, print message and abandon execution.
+    Portability defines for printf formatters:
+    U16_F, S16_F, X16_F, U32_F, S32_F, X32_F, SZT_F
+
+  "lightweight" synchronization mechanisms -
+    SYS_ARCH_DECL_PROTECT(x) - declare a protection state variable.
+    SYS_ARCH_PROTECT(x)      - enter protection mode.
+    SYS_ARCH_UNPROTECT(x)    - leave protection mode.
+
+  If the compiler does not provide memset() this file must include a
+  definition of it, or include a file which defines it.
+
+  This file must either include a system-local <errno.h> which defines
+  the standard *nix error codes, or it should #define LWIP_PROVIDE_ERRNO
+  to make lwip/arch.h define the codes which are used throughout.
+
+
+perf.h     - Architecture specific performance measurement.
+  Measurement calls made throughout lwip, these can be defined to nothing.
+    PERF_START               - start measuring something.
+    PERF_STOP(x)             - stop measuring something, and record the result.
+
+sys_arch.h - Tied to sys_arch.c
+
+  Arch dependent types for the following objects:
+    sys_sem_t, sys_mbox_t, sys_thread_t,
+  And, optionally:
+    sys_prot_t
+
+  Defines to set vars of sys_mbox_t and sys_sem_t to NULL.
+    SYS_MBOX_NULL NULL
+    SYS_SEM_NULL NULL

+ 63 - 0
libs/thirdparty/LwIP/doc°contrib.txt

@@ -0,0 +1,63 @@
+1 Introduction
+
+This document describes some guidelines for people participating
+in lwIP development.
+
+2 How to contribute to lwIP
+
+Here is a short list of suggestions to anybody working with lwIP and 
+trying to contribute bug reports, fixes, enhancements, platform ports etc.
+First of all as you may already know lwIP is a volunteer project so feedback
+to fixes or questions might often come late. Hopefully the bug and patch tracking 
+features of Savannah help us not lose users' input.
+
+2.1 Source code style:
+
+1. do not use tabs.
+2. indentation is two spaces per level (i.e. per tab).
+3. end debug messages with a trailing newline (\n).
+4. one space between keyword and opening bracket.
+5. no space between function and opening bracket.
+6. one space and no newline before opening curly braces of a block.
+7. closing curly brace on a single line.
+8. spaces surrounding assignment and comparisons.
+9. don't initialize static and/or global variables to zero, the compiler takes care of that.
+10. use current source code style as further reference.
+
+2.2 Source code documentation style:
+
+1. JavaDoc compliant and Doxygen compatible.
+2. Function documentation above functions in .c files, not .h files.
+   (This forces you to synchronize documentation and implementation.)
+3. Use current documentation style as further reference.
+ 
+2.3 Bug reports and patches:
+
+1. Make sure you are reporting bugs or send patches against the latest
+   sources. (From the latest release and/or the current CVS sources.)
+2. If you think you found a bug make sure it's not already filed in the
+   bugtracker at Savannah.
+3. If you have a fix put the patch on Savannah. If it is a patch that affects
+   both core and arch specific stuff please separate them so that the core can
+   be applied separately while leaving the other patch 'open'. The prefered way
+   is to NOT touch archs you can't test and let maintainers take care of them.
+   This is a good way to see if they are used at all - the same goes for unix
+   netifs except tapif.
+4. Do not file a bug and post a fix to it to the patch area. Either a bug report
+   or a patch will be enough.
+   If you correct an existing bug then attach the patch to the bug rather than creating a new entry in the patch area.
+5. Trivial patches (compiler warning, indentation and spelling fixes or anything obvious which takes a line or two)
+   can go to the lwip-users list. This is still the fastest way of interaction and the list is not so crowded
+   as to allow for loss of fixes. Putting bugs on Savannah and subsequently closing them is too much an overhead
+   for reporting a compiler warning fix.
+6. Patches should be specific to a single change or to related changes.Do not mix bugfixes with spelling and other
+   trivial fixes unless the bugfix is trivial too.Do not reorganize code and rename identifiers in the same patch you
+   change behaviour if not necessary.A patch is easier to read and understand if it's to the point and short than
+   if it's not to the point and long :) so the chances for it to be applied are greater. 
+
+2.4 Platform porters:
+
+1. If you have ported lwIP to a platform (an OS, a uC/processor or a combination of these) and
+   you think it could benefit others[1] you might want discuss this on the mailing list. You
+   can also ask for CVS access to submit and maintain your port in the contrib CVS module.
+   

+ 0 - 0
libs/thirdparty/lwip_2.1.2/port/FreeRTOS/bpstruct.h → libs/thirdparty/LwIP/port/FreeRTOS/bpstruct.h


+ 0 - 0
libs/thirdparty/lwip_2.1.2/port/FreeRTOS/cc.h → libs/thirdparty/LwIP/port/FreeRTOS/cc.h


+ 0 - 0
libs/thirdparty/lwip_2.1.2/port/FreeRTOS/epstruct.h → libs/thirdparty/LwIP/port/FreeRTOS/epstruct.h


+ 9 - 6
libs/thirdparty/lwip_2.1.2/port/FreeRTOS/ethernetif.c → libs/thirdparty/LwIP/port/FreeRTOS/ethernetif.c

@@ -210,6 +210,7 @@ low_level_output(struct netif *netif, struct pbuf *p)
 static struct pbuf *
 static struct pbuf *
 low_level_input(struct netif *netif)
 low_level_input(struct netif *netif)
 {
 {
+#if 0  
   struct pbuf *p, *q;
   struct pbuf *p, *q;
   u16_t len;
   u16_t len;
   int l =0;
   int l =0;
@@ -217,7 +218,6 @@ low_level_input(struct netif *netif)
   u8 *buffer;
   u8 *buffer;
 
 
   p = NULL;
   p = NULL;
-#if 0  
   frame = emac_rxpkt_chainmode();
   frame = emac_rxpkt_chainmode();
   /* Obtain the size of the packet and put it into the "len"
   /* Obtain the size of the packet and put it into the "len"
      variable. */
      variable. */
@@ -249,8 +249,9 @@ low_level_input(struct netif *netif)
     /* Resume DMA reception */
     /* Resume DMA reception */
     EMAC_DMA->rpd_bit.rpd = FALSE;
     EMAC_DMA->rpd_bit.rpd = FALSE;
   }
   }
-#endif
+
   return p;
   return p;
+#endif  
 }
 }
 
 
 /**
 /**
@@ -352,9 +353,10 @@ ethernetif_init(struct netif *netif)
 *******************************************************************************/
 *******************************************************************************/
 FrameTypeDef emac_rxpkt_chainmode(void)
 FrameTypeDef emac_rxpkt_chainmode(void)
 {
 {
+#if 0  
   u32 framelength = 0;
   u32 framelength = 0;
   FrameTypeDef frame = {0,0};
   FrameTypeDef frame = {0,0};
-#if 0
+
   /* Check if the descriptor is owned by the ETHERNET DMA (when set) or CPU (when reset) */
   /* Check if the descriptor is owned by the ETHERNET DMA (when set) or CPU (when reset) */
   if((dma_rx_desc_to_get->status & EMAC_DMARXDESC_OWN) != (u32)RESET)
   if((dma_rx_desc_to_get->status & EMAC_DMARXDESC_OWN) != (u32)RESET)
   {
   {
@@ -396,8 +398,8 @@ FrameTypeDef emac_rxpkt_chainmode(void)
   /* Selects the next DMA Rx descriptor list for next buffer to read */
   /* Selects the next DMA Rx descriptor list for next buffer to read */
   dma_rx_desc_to_get = (emac_dma_desc_type*) (dma_rx_desc_to_get->buf2nextdescaddr);
   dma_rx_desc_to_get = (emac_dma_desc_type*) (dma_rx_desc_to_get->buf2nextdescaddr);
   /* Return Frame */
   /* Return Frame */
-#endif  
   return (frame);
   return (frame);
+#endif  
 }
 }
 
 
 /*******************************************************************************
 /*******************************************************************************
@@ -410,7 +412,7 @@ FrameTypeDef emac_rxpkt_chainmode(void)
 *******************************************************************************/
 *******************************************************************************/
 error_status emac_txpkt_chainmode(u16 FrameLength)
 error_status emac_txpkt_chainmode(u16 FrameLength)
 {
 {
-#if 0  
+#if 0
   /* Check if the descriptor is owned by the ETHERNET DMA (when set) or CPU (when reset) */
   /* Check if the descriptor is owned by the ETHERNET DMA (when set) or CPU (when reset) */
   if((dma_tx_desc_to_set->status & EMAC_DMATXDESC_OWN) != (u32)RESET)
   if((dma_tx_desc_to_set->status & EMAC_DMATXDESC_OWN) != (u32)RESET)
   {
   {
@@ -440,8 +442,9 @@ error_status emac_txpkt_chainmode(u16 FrameLength)
   /* Selects the next DMA Tx descriptor list for next buffer to send */
   /* Selects the next DMA Tx descriptor list for next buffer to send */
   dma_tx_desc_to_set = (emac_dma_desc_type*) (dma_tx_desc_to_set->buf2nextdescaddr);
   dma_tx_desc_to_set = (emac_dma_desc_type*) (dma_tx_desc_to_set->buf2nextdescaddr);
   /* Return SUCCESS */
   /* Return SUCCESS */
-#endif  
+  
   return SUCCESS;
   return SUCCESS;
+#endif  
 }
 }
 
 
 
 

+ 0 - 0
libs/thirdparty/lwip_2.1.2/port/FreeRTOS/ethernetif.h → libs/thirdparty/LwIP/port/FreeRTOS/ethernetif.h


+ 0 - 0
libs/thirdparty/lwip_2.1.2/port/FreeRTOS/perf.h → libs/thirdparty/LwIP/port/FreeRTOS/perf.h


+ 0 - 0
libs/thirdparty/lwip_2.1.2/port/FreeRTOS/sys_arch.c → libs/thirdparty/LwIP/port/FreeRTOS/sys_arch.c


+ 489 - 0
libs/thirdparty/LwIP/port/Standalone/ethernetif.c

@@ -0,0 +1,489 @@
+/**
+ * @file
+ * Ethernet Interface for standalone applications (without RTOS) - works only for 
+ * ethernet polling mode (polling for ethernet frame reception)
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/mem.h"
+#include "netif/etharp.h"
+#include "ethernetif.h"
+#include "at32f403a_407_emac.h"
+#include "common.h"
+#include "common_config.h"   
+#include "settings_api.h"
+#include "utility.h"
+#include <string.h>
+
+// Network interface name
+#define IFNAME0 's'
+#define IFNAME1 't'
+
+#define EMAC_DMARxDesc_FrameLengthShift  16
+
+/**
+ * Helper struct to hold private data used to operate your ethernet interface.
+ * Keeping the ethernet address of the MAC in this struct is not necessary
+ * as it is already kept in the struct netif.
+ * But this is only an example, anyway...
+ */
+struct ethernetif
+{
+  struct eth_addr *ethaddr;
+  /* Add whatever per-interface state that is needed here. */
+  int unused;
+};
+
+#define EMAC_RXBUFNB        4
+#define EMAC_TXBUFNB        2
+
+
+// Ethernet Rx & Tx DMA Descriptors
+#pragma data_alignment = 4
+emac_dma_desc_type  DMARxDscrTab[EMAC_RXBUFNB];
+#pragma data_alignment = 4
+emac_dma_desc_type  DMATxDscrTab[EMAC_TXBUFNB];
+#pragma data_alignment = 4
+uint8_t Rx_Buff[EMAC_RXBUFNB][EMAC_MAX_PACKET_LENGTH]; 
+#pragma data_alignment = 4
+uint8_t Tx_Buff[EMAC_TXBUFNB][EMAC_MAX_PACKET_LENGTH];
+
+extern emac_dma_desc_type  *dma_tx_desc_to_set;
+extern emac_dma_desc_type  *dma_rx_desc_to_get;
+
+typedef struct
+{
+    u32 length;
+    u32 buffer;
+    emac_dma_desc_type *descriptor;
+    
+} FrameTypeDef;
+
+FrameTypeDef emac_rxpkt_chainmode(void);
+u32 emac_getcurrenttxbuffer(void);
+error_status emac_txpkt_chainmode(u16 FrameLength);
+
+
+#ifdef ETHERNETIF_RTOS
+
+#include "FreeRTOS.h"
+#include "task.h"
+
+SemaphoreHandle_t PHY_RX_xSemaphore;
+SemaphoreHandle_t PHY_TX_xSemaphore;
+
+#define PHY_PRIO            (configMAX_PRIORITIES - 1)
+#define PHY_STK_SIZE        1024
+
+extern struct netif xnetif;
+struct netif *lwip_netif= &xnetif;
+
+TaskHandle_t PHY_Handler;
+
+void network_task(void *params);
+
+
+#endif
+
+
+
+/**
+ * In this function, the hardware should be initialized.
+ * Called from ethernetif_init().
+ *
+ * @param netif the already initialized lwip network interface structure
+ *        for this ethernetif
+ */
+static void low_level_init(struct netif *netif)
+{
+    uint8_t mac[6];
+    
+    // set MAC hardware address length
+    netif->hwaddr_len = ETHARP_HWADDR_LEN;
+    
+    SETTINGS_GetMac(mac);
+    
+    emac_local_address_set(mac);
+    
+    netif->hwaddr[0] =  mac[0];
+    netif->hwaddr[1] =  mac[1];
+    netif->hwaddr[2] =  mac[2];
+    netif->hwaddr[3] =  mac[3];
+    netif->hwaddr[4] =  mac[4];
+    netif->hwaddr[5] =  mac[5];
+    
+    // maximum transfer unit
+    netif->mtu = 1500;
+    
+    // device capabilities
+    // don't set NETIF_FLAG_ETHARP if this device is not an ethernet one
+    netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;
+    
+    // Initialize Tx Descriptors list: Chain Mode
+    emac_dma_descriptor_list_address_set(EMAC_DMA_TRANSMIT, DMATxDscrTab, &Tx_Buff[0][0], EMAC_TXBUFNB);
+    // Initialize Rx Descriptors list: Chain Mode 
+    emac_dma_descriptor_list_address_set(EMAC_DMA_RECEIVE, DMARxDscrTab, &Rx_Buff[0][0], EMAC_RXBUFNB);
+
+
+#ifdef ETHERNETIF_RTOS
+  
+    static bool task_eth_flag = false;
+  
+    if (PHY_RX_xSemaphore == NULL) {
+        PHY_RX_xSemaphore = xSemaphoreCreateCounting(20,0);
+    }
+    
+/*    
+    if (PHY_TX_xSemaphore == NULL) {
+        PHY_TX_xSemaphore = xSemaphoreCreateBinary();
+    }
+*/
+    
+    if (!task_eth_flag) 
+    {
+        xTaskCreate((TaskFunction_t)network_task, (const char*)"PHY",(uint16_t)PHY_STK_SIZE, 
+                    (void*)NULL, (UBaseType_t)PHY_PRIO, (TaskHandle_t*)&PHY_Handler); 
+        
+        task_eth_flag = true;
+    }
+  
+#endif  
+  
+  
+  
+  // Enable MAC and DMA transmission and reception
+  emac_start();
+}
+
+/**
+ * This function should do the actual transmission of the packet. The packet is
+ * contained in the pbuf that is passed to the function. This pbuf
+ * might be chained.
+ *
+ * @param netif the lwip network interface structure for this ethernetif
+ * @param p the MAC packet to send (e.g. IP packet including MAC addresses and type)
+ * @return ERR_OK if the packet could be sent
+ *         an err_t value if the packet couldn't be sent
+ *
+ * @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to
+ *       strange results. You might consider waiting for space in the DMA queue
+ *       to become availale since the stack doesn't retry to send a packet
+ *       dropped because of memory failure (except for the TCP timers).
+ */
+
+static err_t low_level_output(struct netif *netif, struct pbuf *p)
+{
+  (void)netif;
+  struct pbuf *q;
+  int framelength = 0;
+  u8 *buffer =  (u8 *)(dma_tx_desc_to_set->buf1addr);
+  
+  /* copy frame from pbufs to driver buffers */
+  for(q = p; q != NULL; q = q->next) 
+  {
+    memcpy((u8_t*)&buffer[framelength], q->payload, q->len);
+	framelength = framelength + q->len;
+  }
+  
+#if defined (TRAFFIC_ENABLE)  
+  trf_inc(TR_ETH_TX, framelength); // трафик (tx)
+#endif  
+  /* Note: padding and CRC for transmitted frame 
+     are automatically inserted by DMA */
+    
+  /* Prepare transmit descriptors to give to DMA*/ 
+  if(emac_txpkt_chainmode(framelength) == ERROR)
+  {
+    return ERR_MEM;
+  }
+
+  return ERR_OK;
+}
+
+/**
+ * Should allocate a pbuf and transfer the bytes of the incoming
+ * packet from the interface into the pbuf.
+ *
+ * @param netif the lwip network interface structure for this ethernetif
+ * @return a pbuf filled with the received packet (including MAC header)
+ *         NULL on memory error
+ */
+static struct pbuf * low_level_input(struct netif *netif)
+{
+  (void)netif;
+  struct pbuf *p, *q;
+  u16_t len;
+  int l =0;
+  FrameTypeDef frame;
+  u8 *buffer;
+  
+  p = NULL;
+  
+  /* get received frame */
+  frame = emac_rxpkt_chainmode();
+  
+  /* Obtain the size of the packet and put it into the "len" variable. */
+  len = frame.length;
+  buffer = (u8 *)frame.buffer;
+  
+  /* We allocate a pbuf chain of pbufs from the Lwip buffer pool */
+  p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
+  
+  /* copy received frame to pbuf chain */
+  if (p != NULL)
+  {
+    for (q = p; q != NULL; q = q->next)
+    {
+      memcpy((u8_t*)q->payload, (u8_t*)&buffer[l], q->len);
+      l = l + q->len;
+    }
+#if defined (TRAFFIC_ENABLE)    
+    trf_inc(TR_ETH_RX, l); // трафик (rx)
+#endif    
+  }
+  
+  /* Set Own bit of the Rx descriptor Status: gives the buffer back to ETHERNET DMA */
+  frame.descriptor->status = EMAC_DMARXDESC_OWN;
+
+  /* When Rx Buffer unavailable flag is set: clear it and resume reception */
+  if(emac_dma_flag_get(EMAC_DMA_RBU_FLAG))
+  {
+    /* Clear RBUS ETHERNET DMA flag */
+    emac_dma_flag_clear(EMAC_DMA_RBU_FLAG);
+    /* Resume DMA reception */
+    EMAC_DMA->rpd_bit.rpd = FALSE;
+  }
+  return p;
+}
+
+/**
+ * This function should be called when a packet is ready to be read
+ * from the interface. It uses the function low_level_input() that
+ * should handle the actual reception of bytes from the network
+ * interface. Then the type of the received packet is determined and
+ * the appropriate input function is called.
+ *
+ * @param netif the lwip network interface structure for this ethernetif
+ */
+err_t ethernetif_input(struct netif *netif)
+{
+  err_t err;
+  struct pbuf *p;
+
+  /* move received packet into a new pbuf */
+  p = low_level_input(netif);
+
+  /* no packet could be read, silently ignore this */
+  if (p == NULL) return ERR_MEM;
+
+  /* entry point to the LwIP stack */
+  err = netif->input(p, netif);
+  
+  if (err != ERR_OK)
+  {
+    LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n"));
+    pbuf_free(p);
+    p = NULL;
+  }
+  return err;
+}
+
+/**
+ * Should be called at the beginning of the program to set up the
+ * network interface. It calls the function low_level_init() to do the
+ * actual setup of the hardware.
+ *
+ * This function should be passed as a parameter to netif_add().
+ *
+ * @param netif the lwip network interface structure for this ethernetif
+ * @return ERR_OK if the loopif is initialized
+ *         ERR_MEM if private data couldn't be allocated
+ *         any other err_t on error
+ */
+err_t ethernetif_init(struct netif *netif)
+{
+  LWIP_ASSERT("netif != NULL", (netif != NULL));
+  
+#if LWIP_NETIF_HOSTNAME
+  /* Initialize interface hostname */
+  netif->hostname = "lwip";
+#endif /* LWIP_NETIF_HOSTNAME */
+
+  netif->name[0] = IFNAME0;
+  netif->name[1] = IFNAME1;
+  /* We directly use etharp_output() here to save a function call.
+   * You can instead declare your own function an call etharp_output()
+   * from it if you have to do some checks before sending (e.g. if link
+   * is available...) */
+  netif->output = etharp_output;
+  netif->linkoutput = low_level_output;
+
+  /* initialize the hardware */
+  low_level_init(netif);
+
+  return ERR_OK;
+}
+
+
+/*******************************************************************************
+* Function Name  : emac_rxpkt_chainmode
+* Description    : Receives a packet.
+* Input          : None
+* Output         : None
+* Return         : frame: farme size and location
+*******************************************************************************/
+FrameTypeDef emac_rxpkt_chainmode(void)
+{
+  u32 framelength = 0;
+  FrameTypeDef frame = {0,0};
+
+  /* Check if the descriptor is owned by the ETHERNET DMA (when set) or CPU (when reset) */
+  if((dma_rx_desc_to_get->status & EMAC_DMARXDESC_OWN) != (u32)RESET)
+  {
+    frame.length = FALSE;
+
+    if(emac_dma_flag_get(EMAC_DMA_RBU_FLAG))
+    {
+      /* Clear RBUS ETHERNET DMA flag */
+      emac_dma_flag_clear(EMAC_DMA_RBU_FLAG);
+      /* Resume DMA reception */
+      EMAC_DMA->rpd_bit.rpd = FALSE;
+    }
+    /* Return error: OWN bit set */
+    return frame;
+  }
+
+  if(((dma_rx_desc_to_get->status & EMAC_DMATXDESC_ES) == (u32)RESET) &&
+     ((dma_rx_desc_to_get->status & EMAC_DMARXDESC_LS) != (u32)RESET) &&
+     ((dma_rx_desc_to_get->status & EMAC_DMARXDESC_FS) != (u32)RESET))
+  {
+    /* Get the Frame Length of the received packet: substruct 4 bytes of the CRC */
+    framelength = ((dma_rx_desc_to_get->status & EMAC_DMARXDESC_FL) >> EMAC_DMARxDesc_FrameLengthShift) - 4;
+
+    /* Get the addrees of the actual buffer */
+    frame.buffer = dma_rx_desc_to_get->buf1addr;
+  }
+  else
+  {
+    /* Return ERROR */
+    framelength = FALSE;
+  }
+
+  frame.length = framelength;
+
+  frame.descriptor = dma_rx_desc_to_get;
+
+  /* Update the ETHERNET DMA global Rx descriptor with next Rx decriptor */
+  /* Chained Mode */
+  /* Selects the next DMA Rx descriptor list for next buffer to read */
+  dma_rx_desc_to_get = (emac_dma_desc_type*) (dma_rx_desc_to_get->buf2nextdescaddr);
+  /* Return Frame */
+  return (frame);
+}
+
+/*******************************************************************************
+* Function Name  : emac_txpkt_chainmode
+* Description    : Transmits a packet, from application buffer, pointed by ppkt.
+* Input          : - FrameLength: Tx Packet size.
+* Output         : None
+* Return         : ERROR: in case of Tx desc owned by DMA
+*                  SUCCESS: for correct transmission
+*******************************************************************************/
+error_status emac_txpkt_chainmode(u16 FrameLength)
+{
+  /* Check if the descriptor is owned by the ETHERNET DMA (when set) or CPU (when reset) */
+  if((dma_tx_desc_to_set->status & EMAC_DMATXDESC_OWN) != (u32)RESET)
+  {
+    /* Return ERROR: OWN bit set */
+    return ERROR;
+  }
+
+  /* Setting the Frame Length: bits[12:0] */
+  dma_tx_desc_to_set->controlsize = (FrameLength & EMAC_DMATXDESC_TBS1);
+
+  /* Setting the last segment and first segment bits (in this case a frame is transmitted in one descriptor) */
+  dma_tx_desc_to_set->status |= EMAC_DMATXDESC_LS | EMAC_DMATXDESC_FS;
+
+  /* Set Own bit of the Tx descriptor Status: gives the buffer back to ETHERNET DMA */
+  dma_tx_desc_to_set->status |= EMAC_DMATXDESC_OWN;
+  /* When Tx Buffer unavailable flag is set: clear it and resume transmission */
+  if(emac_dma_flag_get(EMAC_DMA_TBU_FLAG))
+  {
+    /* Clear TBUS ETHERNET DMA flag */
+    emac_dma_flag_clear(EMAC_DMA_TBU_FLAG);
+    /* Resume DMA transmission*/
+    EMAC_DMA->tpd_bit.tpd = 0;
+  }
+
+  /* Update the ETHERNET DMA global Tx descriptor with next Tx decriptor */
+  /* Chained Mode */
+  /* Selects the next DMA Tx descriptor list for next buffer to send */
+  dma_tx_desc_to_set = (emac_dma_desc_type*) (dma_tx_desc_to_set->buf2nextdescaddr);
+  /* Return SUCCESS */
+  
+  return SUCCESS;
+}
+
+
+#ifdef ETHERNETIF_RTOS
+
+//
+void network_task(void *params)
+{
+    struct pbuf *p;
+    err_t err;
+    
+    for (;;)
+    {
+        if (xSemaphoreTake(PHY_RX_xSemaphore, portMAX_DELAY) == pdTRUE)
+        {
+            p = low_level_input(lwip_netif);
+        }
+        
+        if (p != NULL)
+        {
+            err = lwip_netif->input(p, lwip_netif);
+        
+            if (err != ERR_OK)
+            {
+                pbuf_free(p);
+                p = NULL;
+            }
+        
+        }
+    }
+}
+#endif
+

+ 11 - 0
libs/thirdparty/LwIP/port/Standalone/ethernetif.h

@@ -0,0 +1,11 @@
+#ifndef __ETHERNETIF_H__
+#define __ETHERNETIF_H__
+
+
+#include "lwip/err.h"
+#include "lwip/netif.h"
+
+err_t ethernetif_init(struct netif *netif);
+err_t ethernetif_input(struct netif *netif);
+
+#endif

BIN
libs/thirdparty/LwIP/src/.DS_Store


+ 13 - 15
libs/thirdparty/lwip_2.1.2/src/FILES → libs/thirdparty/LwIP/src/FILES

@@ -1,15 +1,13 @@
-api/      - The code for the high-level wrapper API. Not needed if
-            you use the lowel-level call-back/raw API.
-
-apps/     - Higher layer applications that are specifically programmed
-            with the lwIP low-level raw API.
-
-core/     - The core of the TPC/IP stack; protocol implementations,
-            memory and buffer management, and the low-level raw API.
-
-include/  - lwIP include files.
-
-netif/    - Generic network interface device drivers are kept here.
-
-For more information on the various subdirectories, check the FILES
-file in each directory.
+api/      - The code for the high-level wrapper API. Not needed if
+            you use the lowel-level call-back/raw API.
+
+core/     - The core of the TPC/IP stack; protocol implementations,
+            memory and buffer management, and the low-level raw API.
+
+include/  - lwIP include files.
+
+netif/    - Generic network interface device drivers are kept here,
+            as well as the ARP module.
+
+For more information on the various subdirectories, check the FILES
+file in each directory.

+ 995 - 1367
libs/thirdparty/lwip_2.1.2/src/api/api_lib.c → libs/thirdparty/LwIP/src/api/api_lib.c

@@ -1,1367 +1,995 @@
-/**
- * @file
- * Sequential API External module
- *
- * @defgroup netconn Netconn API
- * @ingroup sequential_api
- * Thread-safe, to be called from non-TCPIP threads only.
- * TX/RX handling based on @ref netbuf (containing @ref pbuf)
- * to avoid copying data around.
- *
- * @defgroup netconn_common Common functions
- * @ingroup netconn
- * For use with TCP and UDP
- *
- * @defgroup netconn_tcp TCP only
- * @ingroup netconn
- * TCP only functions
- *
- * @defgroup netconn_udp UDP only
- * @ingroup netconn
- * UDP only functions
- */
-
-/*
- * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- *
- * Author: Adam Dunkels <adam@sics.se>
- */
-
-/* This is the part of the API that is linked with
-   the application */
-
-#include "lwip/opt.h"
-
-#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
-
-#include "lwip/api.h"
-#include "lwip/memp.h"
-
-#include "lwip/ip.h"
-#include "lwip/raw.h"
-#include "lwip/udp.h"
-#include "lwip/priv/api_msg.h"
-#include "lwip/priv/tcp_priv.h"
-#include "lwip/priv/tcpip_priv.h"
-
-#ifdef LWIP_HOOK_FILENAME
-#include LWIP_HOOK_FILENAME
-#endif
-
-#include <string.h>
-
-#define API_MSG_VAR_REF(name)               API_VAR_REF(name)
-#define API_MSG_VAR_DECLARE(name)           API_VAR_DECLARE(struct api_msg, name)
-#define API_MSG_VAR_ALLOC(name)             API_VAR_ALLOC(struct api_msg, MEMP_API_MSG, name, ERR_MEM)
-#define API_MSG_VAR_ALLOC_RETURN_NULL(name) API_VAR_ALLOC(struct api_msg, MEMP_API_MSG, name, NULL)
-#define API_MSG_VAR_FREE(name)              API_VAR_FREE(MEMP_API_MSG, name)
-
-#if TCP_LISTEN_BACKLOG
-/* need to allocate API message for accept so empty message pool does not result in event loss
- * see bug #47512: MPU_COMPATIBLE may fail on empty pool */
-#define API_MSG_VAR_ALLOC_ACCEPT(msg) API_MSG_VAR_ALLOC(msg)
-#define API_MSG_VAR_FREE_ACCEPT(msg) API_MSG_VAR_FREE(msg)
-#else /* TCP_LISTEN_BACKLOG */
-#define API_MSG_VAR_ALLOC_ACCEPT(msg)
-#define API_MSG_VAR_FREE_ACCEPT(msg)
-#endif /* TCP_LISTEN_BACKLOG */
-
-#if LWIP_NETCONN_FULLDUPLEX
-#define NETCONN_RECVMBOX_WAITABLE(conn) (sys_mbox_valid(&(conn)->recvmbox) && (((conn)->flags & NETCONN_FLAG_MBOXINVALID) == 0))
-#define NETCONN_ACCEPTMBOX_WAITABLE(conn) (sys_mbox_valid(&(conn)->acceptmbox) && (((conn)->flags & (NETCONN_FLAG_MBOXCLOSED|NETCONN_FLAG_MBOXINVALID)) == 0))
-#define NETCONN_MBOX_WAITING_INC(conn) SYS_ARCH_INC(conn->mbox_threads_waiting, 1)
-#define NETCONN_MBOX_WAITING_DEC(conn) SYS_ARCH_DEC(conn->mbox_threads_waiting, 1)
-#else /* LWIP_NETCONN_FULLDUPLEX */
-#define NETCONN_RECVMBOX_WAITABLE(conn)   sys_mbox_valid(&(conn)->recvmbox)
-#define NETCONN_ACCEPTMBOX_WAITABLE(conn) (sys_mbox_valid(&(conn)->acceptmbox) && (((conn)->flags & NETCONN_FLAG_MBOXCLOSED) == 0))
-#define NETCONN_MBOX_WAITING_INC(conn)
-#define NETCONN_MBOX_WAITING_DEC(conn)
-#endif /* LWIP_NETCONN_FULLDUPLEX */
-
-static err_t netconn_close_shutdown(struct netconn *conn, u8_t how);
-
-/**
- * Call the lower part of a netconn_* function
- * This function is then running in the thread context
- * of tcpip_thread and has exclusive access to lwIP core code.
- *
- * @param fn function to call
- * @param apimsg a struct containing the function to call and its parameters
- * @return ERR_OK if the function was called, another err_t if not
- */
-static err_t
-netconn_apimsg(tcpip_callback_fn fn, struct api_msg *apimsg)
-{
-  err_t err;
-
-#ifdef LWIP_DEBUG
-  /* catch functions that don't set err */
-  apimsg->err = ERR_VAL;
-#endif /* LWIP_DEBUG */
-
-#if LWIP_NETCONN_SEM_PER_THREAD
-  apimsg->op_completed_sem = LWIP_NETCONN_THREAD_SEM_GET();
-#endif /* LWIP_NETCONN_SEM_PER_THREAD */
-
-  err = tcpip_send_msg_wait_sem(fn, apimsg, LWIP_API_MSG_SEM(apimsg));
-  if (err == ERR_OK) {
-    return apimsg->err;
-  }
-  return err;
-}
-
-/**
- * Create a new netconn (of a specific type) that has a callback function.
- * The corresponding pcb is also created.
- *
- * @param t the type of 'connection' to create (@see enum netconn_type)
- * @param proto the IP protocol for RAW IP pcbs
- * @param callback a function to call on status changes (RX available, TX'ed)
- * @return a newly allocated struct netconn or
- *         NULL on memory error
- */
-struct netconn *
-netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto, netconn_callback callback)
-{
-  struct netconn *conn;
-  API_MSG_VAR_DECLARE(msg);
-  API_MSG_VAR_ALLOC_RETURN_NULL(msg);
-
-  conn = netconn_alloc(t, callback);
-  if (conn != NULL) {
-    err_t err;
-
-    API_MSG_VAR_REF(msg).msg.n.proto = proto;
-    API_MSG_VAR_REF(msg).conn = conn;
-    err = netconn_apimsg(lwip_netconn_do_newconn, &API_MSG_VAR_REF(msg));
-    if (err != ERR_OK) {
-      LWIP_ASSERT("freeing conn without freeing pcb", conn->pcb.tcp == NULL);
-      LWIP_ASSERT("conn has no recvmbox", sys_mbox_valid(&conn->recvmbox));
-#if LWIP_TCP
-      LWIP_ASSERT("conn->acceptmbox shouldn't exist", !sys_mbox_valid(&conn->acceptmbox));
-#endif /* LWIP_TCP */
-#if !LWIP_NETCONN_SEM_PER_THREAD
-      LWIP_ASSERT("conn has no op_completed", sys_sem_valid(&conn->op_completed));
-      sys_sem_free(&conn->op_completed);
-#endif /* !LWIP_NETCONN_SEM_PER_THREAD */
-      sys_mbox_free(&conn->recvmbox);
-      memp_free(MEMP_NETCONN, conn);
-      API_MSG_VAR_FREE(msg);
-      return NULL;
-    }
-  }
-  API_MSG_VAR_FREE(msg);
-  return conn;
-}
-
-/**
- * @ingroup netconn_common
- * Close a netconn 'connection' and free all its resources but not the netconn itself.
- * UDP and RAW connection are completely closed, TCP pcbs might still be in a waitstate
- * after this returns.
- *
- * @param conn the netconn to delete
- * @return ERR_OK if the connection was deleted
- */
-err_t
-netconn_prepare_delete(struct netconn *conn)
-{
-  err_t err;
-  API_MSG_VAR_DECLARE(msg);
-
-  /* No ASSERT here because possible to get a (conn == NULL) if we got an accept error */
-  if (conn == NULL) {
-    return ERR_OK;
-  }
-
-  API_MSG_VAR_ALLOC(msg);
-  API_MSG_VAR_REF(msg).conn = conn;
-#if LWIP_SO_SNDTIMEO || LWIP_SO_LINGER
-  /* get the time we started, which is later compared to
-     sys_now() + conn->send_timeout */
-  API_MSG_VAR_REF(msg).msg.sd.time_started = sys_now();
-#else /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
-#if LWIP_TCP
-  API_MSG_VAR_REF(msg).msg.sd.polls_left =
-    ((LWIP_TCP_CLOSE_TIMEOUT_MS_DEFAULT + TCP_SLOW_INTERVAL - 1) / TCP_SLOW_INTERVAL) + 1;
-#endif /* LWIP_TCP */
-#endif /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
-  err = netconn_apimsg(lwip_netconn_do_delconn, &API_MSG_VAR_REF(msg));
-  API_MSG_VAR_FREE(msg);
-
-  if (err != ERR_OK) {
-    return err;
-  }
-  return ERR_OK;
-}
-
-/**
- * @ingroup netconn_common
- * Close a netconn 'connection' and free its resources.
- * UDP and RAW connection are completely closed, TCP pcbs might still be in a waitstate
- * after this returns.
- *
- * @param conn the netconn to delete
- * @return ERR_OK if the connection was deleted
- */
-err_t
-netconn_delete(struct netconn *conn)
-{
-  err_t err;
-
-  /* No ASSERT here because possible to get a (conn == NULL) if we got an accept error */
-  if (conn == NULL) {
-    return ERR_OK;
-  }
-
-#if LWIP_NETCONN_FULLDUPLEX
-  if (conn->flags & NETCONN_FLAG_MBOXINVALID) {
-    /* Already called netconn_prepare_delete() before */
-    err = ERR_OK;
-  } else
-#endif /* LWIP_NETCONN_FULLDUPLEX */
-  {
-    err = netconn_prepare_delete(conn);
-  }
-  if (err == ERR_OK) {
-    netconn_free(conn);
-  }
-  return err;
-}
-
-/**
- * Get the local or remote IP address and port of a netconn.
- * For RAW netconns, this returns the protocol instead of a port!
- *
- * @param conn the netconn to query
- * @param addr a pointer to which to save the IP address
- * @param port a pointer to which to save the port (or protocol for RAW)
- * @param local 1 to get the local IP address, 0 to get the remote one
- * @return ERR_CONN for invalid connections
- *         ERR_OK if the information was retrieved
- */
-err_t
-netconn_getaddr(struct netconn *conn, ip_addr_t *addr, u16_t *port, u8_t local)
-{
-  API_MSG_VAR_DECLARE(msg);
-  err_t err;
-
-  LWIP_ERROR("netconn_getaddr: invalid conn", (conn != NULL), return ERR_ARG;);
-  LWIP_ERROR("netconn_getaddr: invalid addr", (addr != NULL), return ERR_ARG;);
-  LWIP_ERROR("netconn_getaddr: invalid port", (port != NULL), return ERR_ARG;);
-
-  API_MSG_VAR_ALLOC(msg);
-  API_MSG_VAR_REF(msg).conn = conn;
-  API_MSG_VAR_REF(msg).msg.ad.local = local;
-#if LWIP_MPU_COMPATIBLE
-  err = netconn_apimsg(lwip_netconn_do_getaddr, &API_MSG_VAR_REF(msg));
-  *addr = msg->msg.ad.ipaddr;
-  *port = msg->msg.ad.port;
-#else /* LWIP_MPU_COMPATIBLE */
-  msg.msg.ad.ipaddr = addr;
-  msg.msg.ad.port = port;
-  err = netconn_apimsg(lwip_netconn_do_getaddr, &msg);
-#endif /* LWIP_MPU_COMPATIBLE */
-  API_MSG_VAR_FREE(msg);
-
-  return err;
-}
-
-/**
- * @ingroup netconn_common
- * Bind a netconn to a specific local IP address and port.
- * Binding one netconn twice might not always be checked correctly!
- *
- * @param conn the netconn to bind
- * @param addr the local IP address to bind the netconn to
- *             (use IP4_ADDR_ANY/IP6_ADDR_ANY to bind to all addresses)
- * @param port the local port to bind the netconn to (not used for RAW)
- * @return ERR_OK if bound, any other err_t on failure
- */
-err_t
-netconn_bind(struct netconn *conn, const ip_addr_t *addr, u16_t port)
-{
-  API_MSG_VAR_DECLARE(msg);
-  err_t err;
-
-  LWIP_ERROR("netconn_bind: invalid conn", (conn != NULL), return ERR_ARG;);
-
-#if LWIP_IPV4
-  /* Don't propagate NULL pointer (IP_ADDR_ANY alias) to subsequent functions */
-  if (addr == NULL) {
-    addr = IP4_ADDR_ANY;
-  }
-#endif /* LWIP_IPV4 */
-
-#if LWIP_IPV4 && LWIP_IPV6
-  /* "Socket API like" dual-stack support: If IP to bind to is IP6_ADDR_ANY,
-   * and NETCONN_FLAG_IPV6_V6ONLY is 0, use IP_ANY_TYPE to bind
-   */
-  if ((netconn_get_ipv6only(conn) == 0) &&
-      ip_addr_cmp(addr, IP6_ADDR_ANY)) {
-    addr = IP_ANY_TYPE;
-  }
-#endif /* LWIP_IPV4 && LWIP_IPV6 */
-
-  API_MSG_VAR_ALLOC(msg);
-  API_MSG_VAR_REF(msg).conn = conn;
-  API_MSG_VAR_REF(msg).msg.bc.ipaddr = API_MSG_VAR_REF(addr);
-  API_MSG_VAR_REF(msg).msg.bc.port = port;
-  err = netconn_apimsg(lwip_netconn_do_bind, &API_MSG_VAR_REF(msg));
-  API_MSG_VAR_FREE(msg);
-
-  return err;
-}
-
-/**
- * @ingroup netconn_common
- * Bind a netconn to a specific interface and port.
- * Binding one netconn twice might not always be checked correctly!
- *
- * @param conn the netconn to bind
- * @param if_idx the local interface index to bind the netconn to
- * @return ERR_OK if bound, any other err_t on failure
- */
-err_t
-netconn_bind_if(struct netconn *conn, u8_t if_idx)
-{
-  API_MSG_VAR_DECLARE(msg);
-  err_t err;
-
-  LWIP_ERROR("netconn_bind_if: invalid conn", (conn != NULL), return ERR_ARG;);
-
-  API_MSG_VAR_ALLOC(msg);
-  API_MSG_VAR_REF(msg).conn = conn;
-  API_MSG_VAR_REF(msg).msg.bc.if_idx = if_idx;
-  err = netconn_apimsg(lwip_netconn_do_bind_if, &API_MSG_VAR_REF(msg));
-  API_MSG_VAR_FREE(msg);
-
-  return err;
-}
-
-/**
- * @ingroup netconn_common
- * Connect a netconn to a specific remote IP address and port.
- *
- * @param conn the netconn to connect
- * @param addr the remote IP address to connect to
- * @param port the remote port to connect to (no used for RAW)
- * @return ERR_OK if connected, return value of tcp_/udp_/raw_connect otherwise
- */
-err_t
-netconn_connect(struct netconn *conn, const ip_addr_t *addr, u16_t port)
-{
-  API_MSG_VAR_DECLARE(msg);
-  err_t err;
-
-  LWIP_ERROR("netconn_connect: invalid conn", (conn != NULL), return ERR_ARG;);
-
-#if LWIP_IPV4
-  /* Don't propagate NULL pointer (IP_ADDR_ANY alias) to subsequent functions */
-  if (addr == NULL) {
-    addr = IP4_ADDR_ANY;
-  }
-#endif /* LWIP_IPV4 */
-
-  API_MSG_VAR_ALLOC(msg);
-  API_MSG_VAR_REF(msg).conn = conn;
-  API_MSG_VAR_REF(msg).msg.bc.ipaddr = API_MSG_VAR_REF(addr);
-  API_MSG_VAR_REF(msg).msg.bc.port = port;
-  err = netconn_apimsg(lwip_netconn_do_connect, &API_MSG_VAR_REF(msg));
-  API_MSG_VAR_FREE(msg);
-
-  return err;
-}
-
-/**
- * @ingroup netconn_udp
- * Disconnect a netconn from its current peer (only valid for UDP netconns).
- *
- * @param conn the netconn to disconnect
- * @return See @ref err_t
- */
-err_t
-netconn_disconnect(struct netconn *conn)
-{
-  API_MSG_VAR_DECLARE(msg);
-  err_t err;
-
-  LWIP_ERROR("netconn_disconnect: invalid conn", (conn != NULL), return ERR_ARG;);
-
-  API_MSG_VAR_ALLOC(msg);
-  API_MSG_VAR_REF(msg).conn = conn;
-  err = netconn_apimsg(lwip_netconn_do_disconnect, &API_MSG_VAR_REF(msg));
-  API_MSG_VAR_FREE(msg);
-
-  return err;
-}
-
-/**
- * @ingroup netconn_tcp
- * Set a TCP netconn into listen mode
- *
- * @param conn the tcp netconn to set to listen mode
- * @param backlog the listen backlog, only used if TCP_LISTEN_BACKLOG==1
- * @return ERR_OK if the netconn was set to listen (UDP and RAW netconns
- *         don't return any error (yet?))
- */
-err_t
-netconn_listen_with_backlog(struct netconn *conn, u8_t backlog)
-{
-#if LWIP_TCP
-  API_MSG_VAR_DECLARE(msg);
-  err_t err;
-
-  /* This does no harm. If TCP_LISTEN_BACKLOG is off, backlog is unused. */
-  LWIP_UNUSED_ARG(backlog);
-
-  LWIP_ERROR("netconn_listen: invalid conn", (conn != NULL), return ERR_ARG;);
-
-  API_MSG_VAR_ALLOC(msg);
-  API_MSG_VAR_REF(msg).conn = conn;
-#if TCP_LISTEN_BACKLOG
-  API_MSG_VAR_REF(msg).msg.lb.backlog = backlog;
-#endif /* TCP_LISTEN_BACKLOG */
-  err = netconn_apimsg(lwip_netconn_do_listen, &API_MSG_VAR_REF(msg));
-  API_MSG_VAR_FREE(msg);
-
-  return err;
-#else /* LWIP_TCP */
-  LWIP_UNUSED_ARG(conn);
-  LWIP_UNUSED_ARG(backlog);
-  return ERR_ARG;
-#endif /* LWIP_TCP */
-}
-
-/**
- * @ingroup netconn_tcp
- * Accept a new connection on a TCP listening netconn.
- *
- * @param conn the TCP listen netconn
- * @param new_conn pointer where the new connection is stored
- * @return ERR_OK if a new connection has been received or an error
- *                code otherwise
- */
-err_t
-netconn_accept(struct netconn *conn, struct netconn **new_conn)
-{
-#if LWIP_TCP
-  err_t err;
-  void *accept_ptr;
-  struct netconn *newconn;
-#if TCP_LISTEN_BACKLOG
-  API_MSG_VAR_DECLARE(msg);
-#endif /* TCP_LISTEN_BACKLOG */
-
-  LWIP_ERROR("netconn_accept: invalid pointer",    (new_conn != NULL),                  return ERR_ARG;);
-  *new_conn = NULL;
-  LWIP_ERROR("netconn_accept: invalid conn",       (conn != NULL),                      return ERR_ARG;);
-
-  /* NOTE: Although the opengroup spec says a pending error shall be returned to
-           send/recv/getsockopt(SO_ERROR) only, we return it for listening
-           connections also, to handle embedded-system errors */
-  err = netconn_err(conn);
-  if (err != ERR_OK) {
-    /* return pending error */
-    return err;
-  }
-  if (!NETCONN_ACCEPTMBOX_WAITABLE(conn)) {
-    /* don't accept if closed: this might block the application task
-       waiting on acceptmbox forever! */
-    return ERR_CLSD;
-  }
-
-  API_MSG_VAR_ALLOC_ACCEPT(msg);
-
-  NETCONN_MBOX_WAITING_INC(conn);
-  if (netconn_is_nonblocking(conn)) {
-    if (sys_arch_mbox_tryfetch(&conn->acceptmbox, &accept_ptr) == SYS_ARCH_TIMEOUT) {
-      API_MSG_VAR_FREE_ACCEPT(msg);
-      NETCONN_MBOX_WAITING_DEC(conn);
-      return ERR_WOULDBLOCK;
-    }
-  } else {
-#if LWIP_SO_RCVTIMEO
-    if (sys_arch_mbox_fetch(&conn->acceptmbox, &accept_ptr, conn->recv_timeout) == SYS_ARCH_TIMEOUT) {
-      API_MSG_VAR_FREE_ACCEPT(msg);
-      NETCONN_MBOX_WAITING_DEC(conn);
-      return ERR_TIMEOUT;
-    }
-#else
-    sys_arch_mbox_fetch(&conn->acceptmbox, &accept_ptr, 0);
-#endif /* LWIP_SO_RCVTIMEO*/
-  }
-  NETCONN_MBOX_WAITING_DEC(conn);
-#if LWIP_NETCONN_FULLDUPLEX
-  if (conn->flags & NETCONN_FLAG_MBOXINVALID) {
-    if (lwip_netconn_is_deallocated_msg(accept_ptr)) {
-      /* the netconn has been closed from another thread */
-      API_MSG_VAR_FREE_ACCEPT(msg);
-      return ERR_CONN;
-    }
-  }
-#endif
-
-  /* Register event with callback */
-  API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0);
-
-  if (lwip_netconn_is_err_msg(accept_ptr, &err)) {
-    /* a connection has been aborted: e.g. out of pcbs or out of netconns during accept */
-    API_MSG_VAR_FREE_ACCEPT(msg);
-    return err;
-  }
-  if (accept_ptr == NULL) {
-    /* connection has been aborted */
-    API_MSG_VAR_FREE_ACCEPT(msg);
-    return ERR_CLSD;
-  }
-  newconn = (struct netconn *)accept_ptr;
-#if TCP_LISTEN_BACKLOG
-  /* Let the stack know that we have accepted the connection. */
-  API_MSG_VAR_REF(msg).conn = newconn;
-  /* don't care for the return value of lwip_netconn_do_recv */
-  netconn_apimsg(lwip_netconn_do_accepted, &API_MSG_VAR_REF(msg));
-  API_MSG_VAR_FREE(msg);
-#endif /* TCP_LISTEN_BACKLOG */
-
-  *new_conn = newconn;
-  /* don't set conn->last_err: it's only ERR_OK, anyway */
-  return ERR_OK;
-#else /* LWIP_TCP */
-  LWIP_UNUSED_ARG(conn);
-  LWIP_UNUSED_ARG(new_conn);
-  return ERR_ARG;
-#endif /* LWIP_TCP */
-}
-
-/**
- * @ingroup netconn_common
- * Receive data: actual implementation that doesn't care whether pbuf or netbuf
- * is received (this is internal, it's just here for describing common errors)
- *
- * @param conn the netconn from which to receive data
- * @param new_buf pointer where a new pbuf/netbuf is stored when received data
- * @param apiflags flags that control function behaviour. For now only:
- * - NETCONN_DONTBLOCK: only read data that is available now, don't wait for more data
- * @return ERR_OK if data has been received, an error code otherwise (timeout,
- *                memory error or another error)
- *         ERR_CONN if not connected
- *         ERR_CLSD if TCP connection has been closed
- *         ERR_WOULDBLOCK if the netconn is nonblocking but would block to wait for data
- *         ERR_TIMEOUT if the netconn has a receive timeout and no data was received
- */
-static err_t
-netconn_recv_data(struct netconn *conn, void **new_buf, u8_t apiflags)
-{
-  void *buf = NULL;
-  u16_t len;
-
-  LWIP_ERROR("netconn_recv: invalid pointer", (new_buf != NULL), return ERR_ARG;);
-  *new_buf = NULL;
-  LWIP_ERROR("netconn_recv: invalid conn",    (conn != NULL),    return ERR_ARG;);
-
-  if (!NETCONN_RECVMBOX_WAITABLE(conn)) {
-    err_t err = netconn_err(conn);
-    if (err != ERR_OK) {
-      /* return pending error */
-      return err;
-    }
-    return ERR_CONN;
-  }
-
-  NETCONN_MBOX_WAITING_INC(conn);
-  if (netconn_is_nonblocking(conn) || (apiflags & NETCONN_DONTBLOCK) ||
-      (conn->flags & NETCONN_FLAG_MBOXCLOSED) || (conn->pending_err != ERR_OK)) {
-    if (sys_arch_mbox_tryfetch(&conn->recvmbox, &buf) == SYS_ARCH_TIMEOUT) {
-      err_t err;
-      NETCONN_MBOX_WAITING_DEC(conn);
-      err = netconn_err(conn);
-      if (err != ERR_OK) {
-        /* return pending error */
-        return err;
-      }
-      if (conn->flags & NETCONN_FLAG_MBOXCLOSED) {
-        return ERR_CONN;
-      }
-      return ERR_WOULDBLOCK;
-    }
-  } else {
-#if LWIP_SO_RCVTIMEO
-    if (sys_arch_mbox_fetch(&conn->recvmbox, &buf, conn->recv_timeout) == SYS_ARCH_TIMEOUT) {
-      NETCONN_MBOX_WAITING_DEC(conn);
-      return ERR_TIMEOUT;
-    }
-#else
-    sys_arch_mbox_fetch(&conn->recvmbox, &buf, 0);
-#endif /* LWIP_SO_RCVTIMEO*/
-  }
-  NETCONN_MBOX_WAITING_DEC(conn);
-#if LWIP_NETCONN_FULLDUPLEX
-  if (conn->flags & NETCONN_FLAG_MBOXINVALID) {
-    if (lwip_netconn_is_deallocated_msg(buf)) {
-      /* the netconn has been closed from another thread */
-      API_MSG_VAR_FREE_ACCEPT(msg);
-      return ERR_CONN;
-    }
-  }
-#endif
-
-#if LWIP_TCP
-#if (LWIP_UDP || LWIP_RAW)
-  if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP)
-#endif /* (LWIP_UDP || LWIP_RAW) */
-  {
-    err_t err;
-    /* Check if this is an error message or a pbuf */
-    if (lwip_netconn_is_err_msg(buf, &err)) {
-      /* new_buf has been zeroed above already */
-      if (err == ERR_CLSD) {
-        /* connection closed translates to ERR_OK with *new_buf == NULL */
-        return ERR_OK;
-      }
-      return err;
-    }
-    len = ((struct pbuf *)buf)->tot_len;
-  }
-#endif /* LWIP_TCP */
-#if LWIP_TCP && (LWIP_UDP || LWIP_RAW)
-  else
-#endif /* LWIP_TCP && (LWIP_UDP || LWIP_RAW) */
-#if (LWIP_UDP || LWIP_RAW)
-  {
-    LWIP_ASSERT("buf != NULL", buf != NULL);
-    len = netbuf_len((struct netbuf *)buf);
-  }
-#endif /* (LWIP_UDP || LWIP_RAW) */
-
-#if LWIP_SO_RCVBUF
-  SYS_ARCH_DEC(conn->recv_avail, len);
-#endif /* LWIP_SO_RCVBUF */
-  /* Register event with callback */
-  API_EVENT(conn, NETCONN_EVT_RCVMINUS, len);
-
-  LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_recv_data: received %p, len=%"U16_F"\n", buf, len));
-
-  *new_buf = buf;
-  /* don't set conn->last_err: it's only ERR_OK, anyway */
-  return ERR_OK;
-}
-
-#if LWIP_TCP
-static err_t
-netconn_tcp_recvd_msg(struct netconn *conn, size_t len, struct api_msg *msg)
-{
-  LWIP_ERROR("netconn_recv_tcp_pbuf: invalid conn", (conn != NULL) &&
-             NETCONNTYPE_GROUP(netconn_type(conn)) == NETCONN_TCP, return ERR_ARG;);
-
-  msg->conn = conn;
-  msg->msg.r.len = len;
-
-  return netconn_apimsg(lwip_netconn_do_recv, msg);
-}
-
-err_t
-netconn_tcp_recvd(struct netconn *conn, size_t len)
-{
-  err_t err;
-  API_MSG_VAR_DECLARE(msg);
-  LWIP_ERROR("netconn_recv_tcp_pbuf: invalid conn", (conn != NULL) &&
-             NETCONNTYPE_GROUP(netconn_type(conn)) == NETCONN_TCP, return ERR_ARG;);
-
-  API_MSG_VAR_ALLOC(msg);
-  err = netconn_tcp_recvd_msg(conn, len, &API_VAR_REF(msg));
-  API_MSG_VAR_FREE(msg);
-  return err;
-}
-
-static err_t
-netconn_recv_data_tcp(struct netconn *conn, struct pbuf **new_buf, u8_t apiflags)
-{
-  err_t err;
-  struct pbuf *buf;
-  API_MSG_VAR_DECLARE(msg);
-#if LWIP_MPU_COMPATIBLE
-  msg = NULL;
-#endif
-
-  if (!NETCONN_RECVMBOX_WAITABLE(conn)) {
-    /* This only happens when calling this function more than once *after* receiving FIN */
-    return ERR_CONN;
-  }
-  if (netconn_is_flag_set(conn, NETCONN_FIN_RX_PENDING)) {
-    netconn_clear_flags(conn, NETCONN_FIN_RX_PENDING);
-    goto handle_fin;
-  }
-
-  if (!(apiflags & NETCONN_NOAUTORCVD)) {
-    /* need to allocate API message here so empty message pool does not result in event loss
-      * see bug #47512: MPU_COMPATIBLE may fail on empty pool */
-    API_MSG_VAR_ALLOC(msg);
-  }
-
-  err = netconn_recv_data(conn, (void **)new_buf, apiflags);
-  if (err != ERR_OK) {
-    if (!(apiflags & NETCONN_NOAUTORCVD)) {
-      API_MSG_VAR_FREE(msg);
-    }
-    return err;
-  }
-  buf = *new_buf;
-  if (!(apiflags & NETCONN_NOAUTORCVD)) {
-    /* Let the stack know that we have taken the data. */
-    u16_t len = buf ? buf->tot_len : 1;
-    /* don't care for the return value of lwip_netconn_do_recv */
-    /* @todo: this should really be fixed, e.g. by retrying in poll on error */
-    netconn_tcp_recvd_msg(conn, len,  &API_VAR_REF(msg));
-    API_MSG_VAR_FREE(msg);
-  }
-
-  /* If we are closed, we indicate that we no longer wish to use the socket */
-  if (buf == NULL) {
-    if (apiflags & NETCONN_NOFIN) {
-      /* received a FIN but the caller cannot handle it right now:
-         re-enqueue it and return "no data" */
-      netconn_set_flags(conn, NETCONN_FIN_RX_PENDING);
-      return ERR_WOULDBLOCK;
-    } else {
-handle_fin:
-      API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0);
-      if (conn->pcb.ip == NULL) {
-        /* race condition: RST during recv */
-        err = netconn_err(conn);
-        if (err != ERR_OK) {
-          return err;
-        }
-        return ERR_RST;
-      }
-      /* RX side is closed, so deallocate the recvmbox */
-      netconn_close_shutdown(conn, NETCONN_SHUT_RD);
-      /* Don' store ERR_CLSD as conn->err since we are only half-closed */
-      return ERR_CLSD;
-    }
-  }
-  return err;
-}
-
-/**
- * @ingroup netconn_tcp
- * Receive data (in form of a pbuf) from a TCP netconn
- *
- * @param conn the netconn from which to receive data
- * @param new_buf pointer where a new pbuf is stored when received data
- * @return ERR_OK if data has been received, an error code otherwise (timeout,
- *                memory error or another error, @see netconn_recv_data)
- *         ERR_ARG if conn is not a TCP netconn
- */
-err_t
-netconn_recv_tcp_pbuf(struct netconn *conn, struct pbuf **new_buf)
-{
-  LWIP_ERROR("netconn_recv_tcp_pbuf: invalid conn", (conn != NULL) &&
-             NETCONNTYPE_GROUP(netconn_type(conn)) == NETCONN_TCP, return ERR_ARG;);
-
-  return netconn_recv_data_tcp(conn, new_buf, 0);
-}
-
-/**
- * @ingroup netconn_tcp
- * Receive data (in form of a pbuf) from a TCP netconn
- *
- * @param conn the netconn from which to receive data
- * @param new_buf pointer where a new pbuf is stored when received data
- * @param apiflags flags that control function behaviour. For now only:
- * - NETCONN_DONTBLOCK: only read data that is available now, don't wait for more data
- * @return ERR_OK if data has been received, an error code otherwise (timeout,
- *                memory error or another error, @see netconn_recv_data)
- *         ERR_ARG if conn is not a TCP netconn
- */
-err_t
-netconn_recv_tcp_pbuf_flags(struct netconn *conn, struct pbuf **new_buf, u8_t apiflags)
-{
-  LWIP_ERROR("netconn_recv_tcp_pbuf: invalid conn", (conn != NULL) &&
-             NETCONNTYPE_GROUP(netconn_type(conn)) == NETCONN_TCP, return ERR_ARG;);
-
-  return netconn_recv_data_tcp(conn, new_buf, apiflags);
-}
-#endif /* LWIP_TCP */
-
-/**
- * Receive data (in form of a netbuf) from a UDP or RAW netconn
- *
- * @param conn the netconn from which to receive data
- * @param new_buf pointer where a new netbuf is stored when received data
- * @return ERR_OK if data has been received, an error code otherwise (timeout,
- *                memory error or another error)
- *         ERR_ARG if conn is not a UDP/RAW netconn
- */
-err_t
-netconn_recv_udp_raw_netbuf(struct netconn *conn, struct netbuf **new_buf)
-{
-  LWIP_ERROR("netconn_recv_udp_raw_netbuf: invalid conn", (conn != NULL) &&
-             NETCONNTYPE_GROUP(netconn_type(conn)) != NETCONN_TCP, return ERR_ARG;);
-
-  return netconn_recv_data(conn, (void **)new_buf, 0);
-}
-
-/**
- * Receive data (in form of a netbuf) from a UDP or RAW netconn
- *
- * @param conn the netconn from which to receive data
- * @param new_buf pointer where a new netbuf is stored when received data
- * @param apiflags flags that control function behaviour. For now only:
- * - NETCONN_DONTBLOCK: only read data that is available now, don't wait for more data
- * @return ERR_OK if data has been received, an error code otherwise (timeout,
- *                memory error or another error)
- *         ERR_ARG if conn is not a UDP/RAW netconn
- */
-err_t
-netconn_recv_udp_raw_netbuf_flags(struct netconn *conn, struct netbuf **new_buf, u8_t apiflags)
-{
-  LWIP_ERROR("netconn_recv_udp_raw_netbuf: invalid conn", (conn != NULL) &&
-             NETCONNTYPE_GROUP(netconn_type(conn)) != NETCONN_TCP, return ERR_ARG;);
-
-  return netconn_recv_data(conn, (void **)new_buf, apiflags);
-}
-
-/**
- * @ingroup netconn_common
- * Receive data (in form of a netbuf containing a packet buffer) from a netconn
- *
- * @param conn the netconn from which to receive data
- * @param new_buf pointer where a new netbuf is stored when received data
- * @return ERR_OK if data has been received, an error code otherwise (timeout,
- *                memory error or another error)
- */
-err_t
-netconn_recv(struct netconn *conn, struct netbuf **new_buf)
-{
-#if LWIP_TCP
-  struct netbuf *buf = NULL;
-  err_t err;
-#endif /* LWIP_TCP */
-
-  LWIP_ERROR("netconn_recv: invalid pointer", (new_buf != NULL), return ERR_ARG;);
-  *new_buf = NULL;
-  LWIP_ERROR("netconn_recv: invalid conn",    (conn != NULL),    return ERR_ARG;);
-
-#if LWIP_TCP
-#if (LWIP_UDP || LWIP_RAW)
-  if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP)
-#endif /* (LWIP_UDP || LWIP_RAW) */
-  {
-    struct pbuf *p = NULL;
-    /* This is not a listening netconn, since recvmbox is set */
-
-    buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
-    if (buf == NULL) {
-      return ERR_MEM;
-    }
-
-    err = netconn_recv_data_tcp(conn, &p, 0);
-    if (err != ERR_OK) {
-      memp_free(MEMP_NETBUF, buf);
-      return err;
-    }
-    LWIP_ASSERT("p != NULL", p != NULL);
-
-    buf->p = p;
-    buf->ptr = p;
-    buf->port = 0;
-    ip_addr_set_zero(&buf->addr);
-    *new_buf = buf;
-    /* don't set conn->last_err: it's only ERR_OK, anyway */
-    return ERR_OK;
-  }
-#endif /* LWIP_TCP */
-#if LWIP_TCP && (LWIP_UDP || LWIP_RAW)
-  else
-#endif /* LWIP_TCP && (LWIP_UDP || LWIP_RAW) */
-  {
-#if (LWIP_UDP || LWIP_RAW)
-    return netconn_recv_data(conn, (void **)new_buf, 0);
-#endif /* (LWIP_UDP || LWIP_RAW) */
-  }
-}
-
-/**
- * @ingroup netconn_udp
- * Send data (in form of a netbuf) to a specific remote IP address and port.
- * Only to be used for UDP and RAW netconns (not TCP).
- *
- * @param conn the netconn over which to send data
- * @param buf a netbuf containing the data to send
- * @param addr the remote IP address to which to send the data
- * @param port the remote port to which to send the data
- * @return ERR_OK if data was sent, any other err_t on error
- */
-err_t
-netconn_sendto(struct netconn *conn, struct netbuf *buf, const ip_addr_t *addr, u16_t port)
-{
-  if (buf != NULL) {
-    ip_addr_set(&buf->addr, addr);
-    buf->port = port;
-    return netconn_send(conn, buf);
-  }
-  return ERR_VAL;
-}
-
-/**
- * @ingroup netconn_udp
- * Send data over a UDP or RAW netconn (that is already connected).
- *
- * @param conn the UDP or RAW netconn over which to send data
- * @param buf a netbuf containing the data to send
- * @return ERR_OK if data was sent, any other err_t on error
- */
-err_t
-netconn_send(struct netconn *conn, struct netbuf *buf)
-{
-  API_MSG_VAR_DECLARE(msg);
-  err_t err;
-
-  LWIP_ERROR("netconn_send: invalid conn",  (conn != NULL), return ERR_ARG;);
-
-  LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_send: sending %"U16_F" bytes\n", buf->p->tot_len));
-
-  API_MSG_VAR_ALLOC(msg);
-  API_MSG_VAR_REF(msg).conn = conn;
-  API_MSG_VAR_REF(msg).msg.b = buf;
-  err = netconn_apimsg(lwip_netconn_do_send, &API_MSG_VAR_REF(msg));
-  API_MSG_VAR_FREE(msg);
-
-  return err;
-}
-
-/**
- * @ingroup netconn_tcp
- * Send data over a TCP netconn.
- *
- * @param conn the TCP netconn over which to send data
- * @param dataptr pointer to the application buffer that contains the data to send
- * @param size size of the application data to send
- * @param apiflags combination of following flags :
- * - NETCONN_COPY: data will be copied into memory belonging to the stack
- * - NETCONN_MORE: for TCP connection, PSH flag will be set on last segment sent
- * - NETCONN_DONTBLOCK: only write the data if all data can be written at once
- * @param bytes_written pointer to a location that receives the number of written bytes
- * @return ERR_OK if data was sent, any other err_t on error
- */
-err_t
-netconn_write_partly(struct netconn *conn, const void *dataptr, size_t size,
-                     u8_t apiflags, size_t *bytes_written)
-{
-  struct netvector vector;
-  vector.ptr = dataptr;
-  vector.len = size;
-  return netconn_write_vectors_partly(conn, &vector, 1, apiflags, bytes_written);
-}
-
-/**
- * Send vectorized data atomically over a TCP netconn.
- *
- * @param conn the TCP netconn over which to send data
- * @param vectors array of vectors containing data to send
- * @param vectorcnt number of vectors in the array
- * @param apiflags combination of following flags :
- * - NETCONN_COPY: data will be copied into memory belonging to the stack
- * - NETCONN_MORE: for TCP connection, PSH flag will be set on last segment sent
- * - NETCONN_DONTBLOCK: only write the data if all data can be written at once
- * @param bytes_written pointer to a location that receives the number of written bytes
- * @return ERR_OK if data was sent, any other err_t on error
- */
-err_t
-netconn_write_vectors_partly(struct netconn *conn, struct netvector *vectors, u16_t vectorcnt,
-                             u8_t apiflags, size_t *bytes_written)
-{
-  API_MSG_VAR_DECLARE(msg);
-  err_t err;
-  u8_t dontblock;
-  size_t size;
-  int i;
-
-  LWIP_ERROR("netconn_write: invalid conn",  (conn != NULL), return ERR_ARG;);
-  LWIP_ERROR("netconn_write: invalid conn->type",  (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP), return ERR_VAL;);
-  dontblock = netconn_is_nonblocking(conn) || (apiflags & NETCONN_DONTBLOCK);
-#if LWIP_SO_SNDTIMEO
-  if (conn->send_timeout != 0) {
-    dontblock = 1;
-  }
-#endif /* LWIP_SO_SNDTIMEO */
-  if (dontblock && !bytes_written) {
-    /* This implies netconn_write() cannot be used for non-blocking send, since
-       it has no way to return the number of bytes written. */
-    return ERR_VAL;
-  }
-
-  /* sum up the total size */
-  size = 0;
-  for (i = 0; i < vectorcnt; i++) {
-    size += vectors[i].len;
-    if (size < vectors[i].len) {
-      /* overflow */
-      return ERR_VAL;
-    }
-  }
-  if (size == 0) {
-    return ERR_OK;
-  } else if (size > SSIZE_MAX) {
-    ssize_t limited;
-    /* this is required by the socket layer (cannot send full size_t range) */
-    if (!bytes_written) {
-      return ERR_VAL;
-    }
-    /* limit the amount of data to send */
-    limited = SSIZE_MAX;
-    size = (size_t)limited;
-  }
-
-  API_MSG_VAR_ALLOC(msg);
-  /* non-blocking write sends as much  */
-  API_MSG_VAR_REF(msg).conn = conn;
-  API_MSG_VAR_REF(msg).msg.w.vector = vectors;
-  API_MSG_VAR_REF(msg).msg.w.vector_cnt = vectorcnt;
-  API_MSG_VAR_REF(msg).msg.w.vector_off = 0;
-  API_MSG_VAR_REF(msg).msg.w.apiflags = apiflags;
-  API_MSG_VAR_REF(msg).msg.w.len = size;
-  API_MSG_VAR_REF(msg).msg.w.offset = 0;
-#if LWIP_SO_SNDTIMEO
-  if (conn->send_timeout != 0) {
-    /* get the time we started, which is later compared to
-        sys_now() + conn->send_timeout */
-    API_MSG_VAR_REF(msg).msg.w.time_started = sys_now();
-  } else {
-    API_MSG_VAR_REF(msg).msg.w.time_started = 0;
-  }
-#endif /* LWIP_SO_SNDTIMEO */
-
-  /* For locking the core: this _can_ be delayed on low memory/low send buffer,
-     but if it is, this is done inside api_msg.c:do_write(), so we can use the
-     non-blocking version here. */
-  err = netconn_apimsg(lwip_netconn_do_write, &API_MSG_VAR_REF(msg));
-  if (err == ERR_OK) {
-    if (bytes_written != NULL) {
-      *bytes_written = API_MSG_VAR_REF(msg).msg.w.offset;
-    }
-    /* for blocking, check all requested bytes were written, NOTE: send_timeout is
-       treated as dontblock (see dontblock assignment above) */
-    if (!dontblock) {
-      LWIP_ASSERT("do_write failed to write all bytes", API_MSG_VAR_REF(msg).msg.w.offset == size);
-    }
-  }
-  API_MSG_VAR_FREE(msg);
-
-  return err;
-}
-
-/**
- * @ingroup netconn_tcp
- * Close or shutdown a TCP netconn (doesn't delete it).
- *
- * @param conn the TCP netconn to close or shutdown
- * @param how fully close or only shutdown one side?
- * @return ERR_OK if the netconn was closed, any other err_t on error
- */
-static err_t
-netconn_close_shutdown(struct netconn *conn, u8_t how)
-{
-  API_MSG_VAR_DECLARE(msg);
-  err_t err;
-  LWIP_UNUSED_ARG(how);
-
-  LWIP_ERROR("netconn_close: invalid conn",  (conn != NULL), return ERR_ARG;);
-
-  API_MSG_VAR_ALLOC(msg);
-  API_MSG_VAR_REF(msg).conn = conn;
-#if LWIP_TCP
-  /* shutting down both ends is the same as closing */
-  API_MSG_VAR_REF(msg).msg.sd.shut = how;
-#if LWIP_SO_SNDTIMEO || LWIP_SO_LINGER
-  /* get the time we started, which is later compared to
-     sys_now() + conn->send_timeout */
-  API_MSG_VAR_REF(msg).msg.sd.time_started = sys_now();
-#else /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
-  API_MSG_VAR_REF(msg).msg.sd.polls_left =
-    ((LWIP_TCP_CLOSE_TIMEOUT_MS_DEFAULT + TCP_SLOW_INTERVAL - 1) / TCP_SLOW_INTERVAL) + 1;
-#endif /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
-#endif /* LWIP_TCP */
-  err = netconn_apimsg(lwip_netconn_do_close, &API_MSG_VAR_REF(msg));
-  API_MSG_VAR_FREE(msg);
-
-  return err;
-}
-
-/**
- * @ingroup netconn_tcp
- * Close a TCP netconn (doesn't delete it).
- *
- * @param conn the TCP netconn to close
- * @return ERR_OK if the netconn was closed, any other err_t on error
- */
-err_t
-netconn_close(struct netconn *conn)
-{
-  /* shutting down both ends is the same as closing */
-  return netconn_close_shutdown(conn, NETCONN_SHUT_RDWR);
-}
-
-/**
- * @ingroup netconn_common
- * Get and reset pending error on a netconn
- *
- * @param conn the netconn to get the error from
- * @return and pending error or ERR_OK if no error was pending
- */
-err_t
-netconn_err(struct netconn *conn)
-{
-  err_t err;
-  SYS_ARCH_DECL_PROTECT(lev);
-  if (conn == NULL) {
-    return ERR_OK;
-  }
-  SYS_ARCH_PROTECT(lev);
-  err = conn->pending_err;
-  conn->pending_err = ERR_OK;
-  SYS_ARCH_UNPROTECT(lev);
-  return err;
-}
-
-/**
- * @ingroup netconn_tcp
- * Shut down one or both sides of a TCP netconn (doesn't delete it).
- *
- * @param conn the TCP netconn to shut down
- * @param shut_rx shut down the RX side (no more read possible after this)
- * @param shut_tx shut down the TX side (no more write possible after this)
- * @return ERR_OK if the netconn was closed, any other err_t on error
- */
-err_t
-netconn_shutdown(struct netconn *conn, u8_t shut_rx, u8_t shut_tx)
-{
-  return netconn_close_shutdown(conn, (u8_t)((shut_rx ? NETCONN_SHUT_RD : 0) | (shut_tx ? NETCONN_SHUT_WR : 0)));
-}
-
-#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD)
-/**
- * @ingroup netconn_udp
- * Join multicast groups for UDP netconns.
- *
- * @param conn the UDP netconn for which to change multicast addresses
- * @param multiaddr IP address of the multicast group to join or leave
- * @param netif_addr the IP address of the network interface on which to send
- *                  the igmp message
- * @param join_or_leave flag whether to send a join- or leave-message
- * @return ERR_OK if the action was taken, any err_t on error
- */
-err_t
-netconn_join_leave_group(struct netconn *conn,
-                         const ip_addr_t *multiaddr,
-                         const ip_addr_t *netif_addr,
-                         enum netconn_igmp join_or_leave)
-{
-  API_MSG_VAR_DECLARE(msg);
-  err_t err;
-
-  LWIP_ERROR("netconn_join_leave_group: invalid conn",  (conn != NULL), return ERR_ARG;);
-
-  API_MSG_VAR_ALLOC(msg);
-
-#if LWIP_IPV4
-  /* Don't propagate NULL pointer (IP_ADDR_ANY alias) to subsequent functions */
-  if (multiaddr == NULL) {
-    multiaddr = IP4_ADDR_ANY;
-  }
-  if (netif_addr == NULL) {
-    netif_addr = IP4_ADDR_ANY;
-  }
-#endif /* LWIP_IPV4 */
-
-  API_MSG_VAR_REF(msg).conn = conn;
-  API_MSG_VAR_REF(msg).msg.jl.multiaddr = API_MSG_VAR_REF(multiaddr);
-  API_MSG_VAR_REF(msg).msg.jl.netif_addr = API_MSG_VAR_REF(netif_addr);
-  API_MSG_VAR_REF(msg).msg.jl.join_or_leave = join_or_leave;
-  err = netconn_apimsg(lwip_netconn_do_join_leave_group, &API_MSG_VAR_REF(msg));
-  API_MSG_VAR_FREE(msg);
-
-  return err;
-}
-/**
- * @ingroup netconn_udp
- * Join multicast groups for UDP netconns.
- *
- * @param conn the UDP netconn for which to change multicast addresses
- * @param multiaddr IP address of the multicast group to join or leave
- * @param if_idx the index of the netif
- * @param join_or_leave flag whether to send a join- or leave-message
- * @return ERR_OK if the action was taken, any err_t on error
- */
-err_t
-netconn_join_leave_group_netif(struct netconn *conn,
-                               const ip_addr_t *multiaddr,
-                               u8_t if_idx,
-                               enum netconn_igmp join_or_leave)
-{
-  API_MSG_VAR_DECLARE(msg);
-  err_t err;
-
-  LWIP_ERROR("netconn_join_leave_group: invalid conn",  (conn != NULL), return ERR_ARG;);
-
-  API_MSG_VAR_ALLOC(msg);
-
-#if LWIP_IPV4
-  /* Don't propagate NULL pointer (IP_ADDR_ANY alias) to subsequent functions */
-  if (multiaddr == NULL) {
-    multiaddr = IP4_ADDR_ANY;
-  }
-  if (if_idx == NETIF_NO_INDEX) {
-    return ERR_IF;
-  }
-#endif /* LWIP_IPV4 */
-
-  API_MSG_VAR_REF(msg).conn = conn;
-  API_MSG_VAR_REF(msg).msg.jl.multiaddr = API_MSG_VAR_REF(multiaddr);
-  API_MSG_VAR_REF(msg).msg.jl.if_idx = if_idx;
-  API_MSG_VAR_REF(msg).msg.jl.join_or_leave = join_or_leave;
-  err = netconn_apimsg(lwip_netconn_do_join_leave_group_netif, &API_MSG_VAR_REF(msg));
-  API_MSG_VAR_FREE(msg);
-
-  return err;
-}
-#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */
-
-#if LWIP_DNS
-/**
- * @ingroup netconn_common
- * Execute a DNS query, only one IP address is returned
- *
- * @param name a string representation of the DNS host name to query
- * @param addr a preallocated ip_addr_t where to store the resolved IP address
- * @param dns_addrtype IP address type (IPv4 / IPv6)
- * @return ERR_OK: resolving succeeded
- *         ERR_MEM: memory error, try again later
- *         ERR_ARG: dns client not initialized or invalid hostname
- *         ERR_VAL: dns server response was invalid
- */
-#if LWIP_IPV4 && LWIP_IPV6
-err_t
-netconn_gethostbyname_addrtype(const char *name, ip_addr_t *addr, u8_t dns_addrtype)
-#else
-err_t
-netconn_gethostbyname(const char *name, ip_addr_t *addr)
-#endif
-{
-  API_VAR_DECLARE(struct dns_api_msg, msg);
-#if !LWIP_MPU_COMPATIBLE
-  sys_sem_t sem;
-#endif /* LWIP_MPU_COMPATIBLE */
-  err_t err;
-  err_t cberr;
-
-  LWIP_ERROR("netconn_gethostbyname: invalid name", (name != NULL), return ERR_ARG;);
-  LWIP_ERROR("netconn_gethostbyname: invalid addr", (addr != NULL), return ERR_ARG;);
-#if LWIP_MPU_COMPATIBLE
-  if (strlen(name) >= DNS_MAX_NAME_LENGTH) {
-    return ERR_ARG;
-  }
-#endif
-
-#ifdef LWIP_HOOK_NETCONN_EXTERNAL_RESOLVE
-#if LWIP_IPV4 && LWIP_IPV6
-  if (LWIP_HOOK_NETCONN_EXTERNAL_RESOLVE(name, addr, dns_addrtype, &err)) {
-#else
-  if (LWIP_HOOK_NETCONN_EXTERNAL_RESOLVE(name, addr, NETCONN_DNS_DEFAULT, &err)) {
-#endif /* LWIP_IPV4 && LWIP_IPV6 */
-    return err;
-  }
-#endif /* LWIP_HOOK_NETCONN_EXTERNAL_RESOLVE */
-
-  API_VAR_ALLOC(struct dns_api_msg, MEMP_DNS_API_MSG, msg, ERR_MEM);
-#if LWIP_MPU_COMPATIBLE
-  strncpy(API_VAR_REF(msg).name, name, DNS_MAX_NAME_LENGTH - 1);
-  API_VAR_REF(msg).name[DNS_MAX_NAME_LENGTH - 1] = 0;
-#else /* LWIP_MPU_COMPATIBLE */
-  msg.err = &err;
-  msg.sem = &sem;
-  API_VAR_REF(msg).addr = API_VAR_REF(addr);
-  API_VAR_REF(msg).name = name;
-#endif /* LWIP_MPU_COMPATIBLE */
-#if LWIP_IPV4 && LWIP_IPV6
-  API_VAR_REF(msg).dns_addrtype = dns_addrtype;
-#endif /* LWIP_IPV4 && LWIP_IPV6 */
-#if LWIP_NETCONN_SEM_PER_THREAD
-  API_VAR_REF(msg).sem = LWIP_NETCONN_THREAD_SEM_GET();
-#else /* LWIP_NETCONN_SEM_PER_THREAD*/
-  err = sys_sem_new(API_EXPR_REF(API_VAR_REF(msg).sem), 0);
-  if (err != ERR_OK) {
-    API_VAR_FREE(MEMP_DNS_API_MSG, msg);
-    return err;
-  }
-#endif /* LWIP_NETCONN_SEM_PER_THREAD */
-
-  cberr = tcpip_send_msg_wait_sem(lwip_netconn_do_gethostbyname, &API_VAR_REF(msg), API_EXPR_REF(API_VAR_REF(msg).sem));
-#if !LWIP_NETCONN_SEM_PER_THREAD
-  sys_sem_free(API_EXPR_REF(API_VAR_REF(msg).sem));
-#endif /* !LWIP_NETCONN_SEM_PER_THREAD */
-  if (cberr != ERR_OK) {
-    API_VAR_FREE(MEMP_DNS_API_MSG, msg);
-    return cberr;
-  }
-
-#if LWIP_MPU_COMPATIBLE
-  *addr = msg->addr;
-  err = msg->err;
-#endif /* LWIP_MPU_COMPATIBLE */
-
-  API_VAR_FREE(MEMP_DNS_API_MSG, msg);
-  return err;
-}
-#endif /* LWIP_DNS*/
-
-#if LWIP_NETCONN_SEM_PER_THREAD
-void
-netconn_thread_init(void)
-{
-  sys_sem_t *sem = LWIP_NETCONN_THREAD_SEM_GET();
-  if ((sem == NULL) || !sys_sem_valid(sem)) {
-    /* call alloc only once */
-    LWIP_NETCONN_THREAD_SEM_ALLOC();
-    LWIP_ASSERT("LWIP_NETCONN_THREAD_SEM_ALLOC() failed", sys_sem_valid(LWIP_NETCONN_THREAD_SEM_GET()));
-  }
-}
-
-void
-netconn_thread_cleanup(void)
-{
-  sys_sem_t *sem = LWIP_NETCONN_THREAD_SEM_GET();
-  if ((sem != NULL) && sys_sem_valid(sem)) {
-    /* call free only once */
-    LWIP_NETCONN_THREAD_SEM_FREE();
-  }
-}
-#endif /* LWIP_NETCONN_SEM_PER_THREAD */
-
-#endif /* LWIP_NETCONN */
+/**
+ * @file
+ * Sequential API External module
+ * 
+ * @defgroup netconn Netconn API
+ * @ingroup sequential_api
+ * Thread-safe, to be called from non-TCPIP threads only.
+ * TX/RX handling based on @ref netbuf (containing @ref pbuf)
+ * to avoid copying data around.
+ * 
+ * @defgroup netconn_common Common functions
+ * @ingroup netconn
+ * For use with TCP and UDP
+ * 
+ * @defgroup netconn_tcp TCP only
+ * @ingroup netconn
+ * TCP only functions
+ * 
+ * @defgroup netconn_udp UDP only
+ * @ingroup netconn
+ * UDP only functions
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ */
+
+/* This is the part of the API that is linked with
+   the application */
+
+#include "lwip/opt.h"
+
+#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/api.h"
+#include "lwip/memp.h"
+
+#include "lwip/ip.h"
+#include "lwip/raw.h"
+#include "lwip/udp.h"
+#include "lwip/priv/api_msg.h"
+#include "lwip/priv/tcp_priv.h"
+#include "lwip/priv/tcpip_priv.h"
+
+#include <string.h>
+
+#define API_MSG_VAR_REF(name)               API_VAR_REF(name)
+#define API_MSG_VAR_DECLARE(name)           API_VAR_DECLARE(struct api_msg, name)
+#define API_MSG_VAR_ALLOC(name)             API_VAR_ALLOC(struct api_msg, MEMP_API_MSG, name, ERR_MEM)
+#define API_MSG_VAR_ALLOC_RETURN_NULL(name) API_VAR_ALLOC(struct api_msg, MEMP_API_MSG, name, NULL)
+#define API_MSG_VAR_FREE(name)              API_VAR_FREE(MEMP_API_MSG, name)
+
+static err_t netconn_close_shutdown(struct netconn *conn, u8_t how);
+
+/**
+ * Call the lower part of a netconn_* function
+ * This function is then running in the thread context
+ * of tcpip_thread and has exclusive access to lwIP core code.
+ *
+ * @param fn function to call
+ * @param apimsg a struct containing the function to call and its parameters
+ * @return ERR_OK if the function was called, another err_t if not
+ */
+static err_t
+netconn_apimsg(tcpip_callback_fn fn, struct api_msg *apimsg)
+{
+  err_t err;
+
+#ifdef LWIP_DEBUG
+  /* catch functions that don't set err */
+  apimsg->err = ERR_VAL;
+#endif /* LWIP_DEBUG */
+
+#if LWIP_NETCONN_SEM_PER_THREAD
+  apimsg->op_completed_sem = LWIP_NETCONN_THREAD_SEM_GET();
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+  err = tcpip_send_msg_wait_sem(fn, apimsg, LWIP_API_MSG_SEM(apimsg));
+  if (err == ERR_OK) {
+    return apimsg->err;
+  }
+  return err;
+}
+
+/**
+ * Create a new netconn (of a specific type) that has a callback function.
+ * The corresponding pcb is also created.
+ *
+ * @param t the type of 'connection' to create (@see enum netconn_type)
+ * @param proto the IP protocol for RAW IP pcbs
+ * @param callback a function to call on status changes (RX available, TX'ed)
+ * @return a newly allocated struct netconn or
+ *         NULL on memory error
+ */
+struct netconn*
+netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto, netconn_callback callback)
+{
+  struct netconn *conn;
+  API_MSG_VAR_DECLARE(msg);
+  API_MSG_VAR_ALLOC_RETURN_NULL(msg);
+
+  conn = netconn_alloc(t, callback);
+  if (conn != NULL) {
+    err_t err;
+
+    API_MSG_VAR_REF(msg).msg.n.proto = proto;
+    API_MSG_VAR_REF(msg).conn = conn;
+    err = netconn_apimsg(lwip_netconn_do_newconn, &API_MSG_VAR_REF(msg));
+    if (err != ERR_OK) {
+      LWIP_ASSERT("freeing conn without freeing pcb", conn->pcb.tcp == NULL);
+      LWIP_ASSERT("conn has no recvmbox", sys_mbox_valid(&conn->recvmbox));
+#if LWIP_TCP
+      LWIP_ASSERT("conn->acceptmbox shouldn't exist", !sys_mbox_valid(&conn->acceptmbox));
+#endif /* LWIP_TCP */
+#if !LWIP_NETCONN_SEM_PER_THREAD
+      LWIP_ASSERT("conn has no op_completed", sys_sem_valid(&conn->op_completed));
+      sys_sem_free(&conn->op_completed);
+#endif /* !LWIP_NETCONN_SEM_PER_THREAD */
+      sys_mbox_free(&conn->recvmbox);
+      memp_free(MEMP_NETCONN, conn);
+      API_MSG_VAR_FREE(msg);
+      return NULL;
+    }
+  }
+  API_MSG_VAR_FREE(msg);
+  return conn;
+}
+
+/**
+ * @ingroup netconn_common
+ * Close a netconn 'connection' and free its resources.
+ * UDP and RAW connection are completely closed, TCP pcbs might still be in a waitstate
+ * after this returns.
+ *
+ * @param conn the netconn to delete
+ * @return ERR_OK if the connection was deleted
+ */
+err_t
+netconn_delete(struct netconn *conn)
+{
+  err_t err;
+  API_MSG_VAR_DECLARE(msg);
+
+  /* No ASSERT here because possible to get a (conn == NULL) if we got an accept error */
+  if (conn == NULL) {
+    return ERR_OK;
+  }
+
+  API_MSG_VAR_ALLOC(msg);
+  API_MSG_VAR_REF(msg).conn = conn;
+#if LWIP_SO_SNDTIMEO || LWIP_SO_LINGER
+  /* get the time we started, which is later compared to
+     sys_now() + conn->send_timeout */
+  API_MSG_VAR_REF(msg).msg.sd.time_started = sys_now();
+#else /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
+#if LWIP_TCP
+  API_MSG_VAR_REF(msg).msg.sd.polls_left =
+    ((LWIP_TCP_CLOSE_TIMEOUT_MS_DEFAULT + TCP_SLOW_INTERVAL - 1) / TCP_SLOW_INTERVAL) + 1;
+#endif /* LWIP_TCP */
+#endif /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
+  err = netconn_apimsg(lwip_netconn_do_delconn, &API_MSG_VAR_REF(msg));
+  API_MSG_VAR_FREE(msg);
+
+  if (err != ERR_OK) {
+    return err;
+  }
+
+  netconn_free(conn);
+
+  return ERR_OK;
+}
+
+/**
+ * Get the local or remote IP address and port of a netconn.
+ * For RAW netconns, this returns the protocol instead of a port!
+ *
+ * @param conn the netconn to query
+ * @param addr a pointer to which to save the IP address
+ * @param port a pointer to which to save the port (or protocol for RAW)
+ * @param local 1 to get the local IP address, 0 to get the remote one
+ * @return ERR_CONN for invalid connections
+ *         ERR_OK if the information was retrieved
+ */
+err_t
+netconn_getaddr(struct netconn *conn, ip_addr_t *addr, u16_t *port, u8_t local)
+{
+  API_MSG_VAR_DECLARE(msg);
+  err_t err;
+
+  LWIP_ERROR("netconn_getaddr: invalid conn", (conn != NULL), return ERR_ARG;);
+  LWIP_ERROR("netconn_getaddr: invalid addr", (addr != NULL), return ERR_ARG;);
+  LWIP_ERROR("netconn_getaddr: invalid port", (port != NULL), return ERR_ARG;);
+
+  API_MSG_VAR_ALLOC(msg);
+  API_MSG_VAR_REF(msg).conn = conn;
+  API_MSG_VAR_REF(msg).msg.ad.local = local;
+#if LWIP_MPU_COMPATIBLE
+  err = netconn_apimsg(lwip_netconn_do_getaddr, &API_MSG_VAR_REF(msg));
+  *addr = msg->msg.ad.ipaddr;
+  *port = msg->msg.ad.port;
+#else /* LWIP_MPU_COMPATIBLE */
+  msg.msg.ad.ipaddr = addr;
+  msg.msg.ad.port = port;
+  err = netconn_apimsg(lwip_netconn_do_getaddr, &msg);
+#endif /* LWIP_MPU_COMPATIBLE */
+  API_MSG_VAR_FREE(msg);
+
+  return err;
+}
+
+/**
+ * @ingroup netconn_common
+ * Bind a netconn to a specific local IP address and port.
+ * Binding one netconn twice might not always be checked correctly!
+ *
+ * @param conn the netconn to bind
+ * @param addr the local IP address to bind the netconn to 
+ *             (use IP4_ADDR_ANY/IP6_ADDR_ANY to bind to all addresses)
+ * @param port the local port to bind the netconn to (not used for RAW)
+ * @return ERR_OK if bound, any other err_t on failure
+ */
+err_t
+netconn_bind(struct netconn *conn, const ip_addr_t *addr, u16_t port)
+{
+  API_MSG_VAR_DECLARE(msg);
+  err_t err;
+  
+  LWIP_ERROR("netconn_bind: invalid conn", (conn != NULL), return ERR_ARG;);
+
+  /* Don't propagate NULL pointer (IP_ADDR_ANY alias) to subsequent functions */
+  if (addr == NULL) {
+    addr = IP4_ADDR_ANY;
+  }
+
+  API_MSG_VAR_ALLOC(msg);
+  API_MSG_VAR_REF(msg).conn = conn;
+  API_MSG_VAR_REF(msg).msg.bc.ipaddr = API_MSG_VAR_REF(addr);
+  API_MSG_VAR_REF(msg).msg.bc.port = port;
+  err = netconn_apimsg(lwip_netconn_do_bind, &API_MSG_VAR_REF(msg));
+  API_MSG_VAR_FREE(msg);
+
+  return err;
+}
+
+/**
+ * @ingroup netconn_common
+ * Connect a netconn to a specific remote IP address and port.
+ *
+ * @param conn the netconn to connect
+ * @param addr the remote IP address to connect to
+ * @param port the remote port to connect to (no used for RAW)
+ * @return ERR_OK if connected, return value of tcp_/udp_/raw_connect otherwise
+ */
+err_t
+netconn_connect(struct netconn *conn, const ip_addr_t *addr, u16_t port)
+{
+  API_MSG_VAR_DECLARE(msg);
+  err_t err;
+
+  LWIP_ERROR("netconn_connect: invalid conn", (conn != NULL), return ERR_ARG;);
+
+  /* Don't propagate NULL pointer (IP_ADDR_ANY alias) to subsequent functions */
+  if (addr == NULL) {
+    addr = IP4_ADDR_ANY;
+  }
+
+  API_MSG_VAR_ALLOC(msg);
+  API_MSG_VAR_REF(msg).conn = conn;
+  API_MSG_VAR_REF(msg).msg.bc.ipaddr = API_MSG_VAR_REF(addr);
+  API_MSG_VAR_REF(msg).msg.bc.port = port;
+  err = netconn_apimsg(lwip_netconn_do_connect, &API_MSG_VAR_REF(msg));
+  API_MSG_VAR_FREE(msg);
+
+  return err;
+}
+
+/**
+ * @ingroup netconn_udp
+ * Disconnect a netconn from its current peer (only valid for UDP netconns).
+ *
+ * @param conn the netconn to disconnect
+ * @return See @ref err_t
+ */
+err_t
+netconn_disconnect(struct netconn *conn)
+{
+  API_MSG_VAR_DECLARE(msg);
+  err_t err;
+
+  LWIP_ERROR("netconn_disconnect: invalid conn", (conn != NULL), return ERR_ARG;);
+
+  API_MSG_VAR_ALLOC(msg);
+  API_MSG_VAR_REF(msg).conn = conn;
+  err = netconn_apimsg(lwip_netconn_do_disconnect, &API_MSG_VAR_REF(msg));
+  API_MSG_VAR_FREE(msg);
+
+  return err;
+}
+
+/**
+ * @ingroup netconn_tcp
+ * Set a TCP netconn into listen mode
+ *
+ * @param conn the tcp netconn to set to listen mode
+ * @param backlog the listen backlog, only used if TCP_LISTEN_BACKLOG==1
+ * @return ERR_OK if the netconn was set to listen (UDP and RAW netconns
+ *         don't return any error (yet?))
+ */
+err_t
+netconn_listen_with_backlog(struct netconn *conn, u8_t backlog)
+{
+#if LWIP_TCP
+  API_MSG_VAR_DECLARE(msg);
+  err_t err;
+
+  /* This does no harm. If TCP_LISTEN_BACKLOG is off, backlog is unused. */
+  LWIP_UNUSED_ARG(backlog);
+
+  LWIP_ERROR("netconn_listen: invalid conn", (conn != NULL), return ERR_ARG;);
+
+  API_MSG_VAR_ALLOC(msg);
+  API_MSG_VAR_REF(msg).conn = conn;
+#if TCP_LISTEN_BACKLOG
+  API_MSG_VAR_REF(msg).msg.lb.backlog = backlog;
+#endif /* TCP_LISTEN_BACKLOG */
+  err = netconn_apimsg(lwip_netconn_do_listen, &API_MSG_VAR_REF(msg));
+  API_MSG_VAR_FREE(msg);
+
+  return err;
+#else /* LWIP_TCP */
+  LWIP_UNUSED_ARG(conn);
+  LWIP_UNUSED_ARG(backlog);
+  return ERR_ARG;
+#endif /* LWIP_TCP */
+}
+
+/**
+ * @ingroup netconn_tcp
+ * Accept a new connection on a TCP listening netconn.
+ *
+ * @param conn the TCP listen netconn
+ * @param new_conn pointer where the new connection is stored
+ * @return ERR_OK if a new connection has been received or an error
+ *                code otherwise
+ */
+err_t
+netconn_accept(struct netconn *conn, struct netconn **new_conn)
+{
+#if LWIP_TCP
+  void *accept_ptr;
+  struct netconn *newconn;
+  err_t err;
+#if TCP_LISTEN_BACKLOG
+  API_MSG_VAR_DECLARE(msg);
+#endif /* TCP_LISTEN_BACKLOG */
+
+  LWIP_ERROR("netconn_accept: invalid pointer",    (new_conn != NULL),                  return ERR_ARG;);
+  *new_conn = NULL;
+  LWIP_ERROR("netconn_accept: invalid conn",       (conn != NULL),                      return ERR_ARG;);
+
+  err = conn->last_err;
+  if (ERR_IS_FATAL(err)) {
+    /* don't recv on fatal errors: this might block the application task
+       waiting on acceptmbox forever! */
+    return err;
+  }
+  if (!sys_mbox_valid(&conn->acceptmbox)) {
+    return ERR_CLSD;
+  }
+
+#if TCP_LISTEN_BACKLOG
+  API_MSG_VAR_ALLOC(msg);
+#endif /* TCP_LISTEN_BACKLOG */
+
+#if LWIP_SO_RCVTIMEO
+  if (sys_arch_mbox_fetch(&conn->acceptmbox, &accept_ptr, conn->recv_timeout) == SYS_ARCH_TIMEOUT) {
+#if TCP_LISTEN_BACKLOG
+    API_MSG_VAR_FREE(msg);
+#endif /* TCP_LISTEN_BACKLOG */
+    return ERR_TIMEOUT;
+  }
+#else
+  sys_arch_mbox_fetch(&conn->acceptmbox, &accept_ptr, 0);
+#endif /* LWIP_SO_RCVTIMEO*/
+  newconn = (struct netconn *)accept_ptr;
+  /* Register event with callback */
+  API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0);
+
+  if (accept_ptr == &netconn_aborted) {
+    /* a connection has been aborted: out of pcbs or out of netconns during accept */
+    /* @todo: set netconn error, but this would be fatal and thus block further accepts */
+#if TCP_LISTEN_BACKLOG
+    API_MSG_VAR_FREE(msg);
+#endif /* TCP_LISTEN_BACKLOG */
+    return ERR_ABRT;
+  }
+  if (newconn == NULL) {
+    /* connection has been aborted */
+    /* in this special case, we set the netconn error from application thread, as
+       on a ready-to-accept listening netconn, there should not be anything running
+       in tcpip_thread */
+    NETCONN_SET_SAFE_ERR(conn, ERR_CLSD);
+#if TCP_LISTEN_BACKLOG
+    API_MSG_VAR_FREE(msg);
+#endif /* TCP_LISTEN_BACKLOG */
+    return ERR_CLSD;
+  }
+#if TCP_LISTEN_BACKLOG
+  /* Let the stack know that we have accepted the connection. */
+  API_MSG_VAR_REF(msg).conn = newconn;
+  /* don't care for the return value of lwip_netconn_do_recv */
+  netconn_apimsg(lwip_netconn_do_accepted, &API_MSG_VAR_REF(msg));
+  API_MSG_VAR_FREE(msg);
+#endif /* TCP_LISTEN_BACKLOG */
+
+  *new_conn = newconn;
+  /* don't set conn->last_err: it's only ERR_OK, anyway */
+  return ERR_OK;
+#else /* LWIP_TCP */
+  LWIP_UNUSED_ARG(conn);
+  LWIP_UNUSED_ARG(new_conn);
+  return ERR_ARG;
+#endif /* LWIP_TCP */
+}
+
+/**
+ * @ingroup netconn_common
+ * Receive data: actual implementation that doesn't care whether pbuf or netbuf
+ * is received
+ *
+ * @param conn the netconn from which to receive data
+ * @param new_buf pointer where a new pbuf/netbuf is stored when received data
+ * @return ERR_OK if data has been received, an error code otherwise (timeout,
+ *                memory error or another error)
+ */
+static err_t
+netconn_recv_data(struct netconn *conn, void **new_buf)
+{
+  void *buf = NULL;
+  u16_t len;
+  err_t err;
+#if LWIP_TCP
+  API_MSG_VAR_DECLARE(msg);
+#if LWIP_MPU_COMPATIBLE
+  msg = NULL;
+#endif
+#endif /* LWIP_TCP */
+
+  LWIP_ERROR("netconn_recv: invalid pointer", (new_buf != NULL), return ERR_ARG;);
+  *new_buf = NULL;
+  LWIP_ERROR("netconn_recv: invalid conn",    (conn != NULL),    return ERR_ARG;);
+#if LWIP_TCP
+#if (LWIP_UDP || LWIP_RAW)
+  if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP)
+#endif /* (LWIP_UDP || LWIP_RAW) */
+  {
+    if (!sys_mbox_valid(&conn->recvmbox)) {
+      /* This happens when calling this function after receiving FIN */
+      return sys_mbox_valid(&conn->acceptmbox) ? ERR_CONN : ERR_CLSD;
+    }
+  }
+#endif /* LWIP_TCP */
+  LWIP_ERROR("netconn_recv: invalid recvmbox", sys_mbox_valid(&conn->recvmbox), return ERR_CONN;);
+
+  err = conn->last_err;
+  if (ERR_IS_FATAL(err)) {
+    /* don't recv on fatal errors: this might block the application task
+       waiting on recvmbox forever! */
+    /* @todo: this does not allow us to fetch data that has been put into recvmbox
+       before the fatal error occurred - is that a problem? */
+    return err;
+  }
+#if LWIP_TCP
+#if (LWIP_UDP || LWIP_RAW)
+  if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP)
+#endif /* (LWIP_UDP || LWIP_RAW) */
+  {
+    API_MSG_VAR_ALLOC(msg);
+  }
+#endif /* LWIP_TCP */
+
+#if LWIP_SO_RCVTIMEO
+  if (sys_arch_mbox_fetch(&conn->recvmbox, &buf, conn->recv_timeout) == SYS_ARCH_TIMEOUT) {
+#if LWIP_TCP
+#if (LWIP_UDP || LWIP_RAW)
+    if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP)
+#endif /* (LWIP_UDP || LWIP_RAW) */
+    {
+      API_MSG_VAR_FREE(msg);
+    }
+#endif /* LWIP_TCP */
+    return ERR_TIMEOUT;
+  }
+#else
+  sys_arch_mbox_fetch(&conn->recvmbox, &buf, 0);
+#endif /* LWIP_SO_RCVTIMEO*/
+
+#if LWIP_TCP
+#if (LWIP_UDP || LWIP_RAW)
+  if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP)
+#endif /* (LWIP_UDP || LWIP_RAW) */
+  {
+    /* Let the stack know that we have taken the data. */
+    /* @todo: Speedup: Don't block and wait for the answer here
+       (to prevent multiple thread-switches). */
+    API_MSG_VAR_REF(msg).conn = conn;
+    if (buf != NULL) {
+      API_MSG_VAR_REF(msg).msg.r.len = ((struct pbuf *)buf)->tot_len;
+    } else {
+      API_MSG_VAR_REF(msg).msg.r.len = 1;
+    }
+
+    /* don't care for the return value of lwip_netconn_do_recv */
+    netconn_apimsg(lwip_netconn_do_recv, &API_MSG_VAR_REF(msg));
+    API_MSG_VAR_FREE(msg);
+
+    /* If we are closed, we indicate that we no longer wish to use the socket */
+    if (buf == NULL) {
+      API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0);
+      if (conn->pcb.ip == NULL) {
+        /* race condition: RST during recv */
+        return conn->last_err == ERR_OK ? ERR_RST : conn->last_err;
+      }
+      /* RX side is closed, so deallocate the recvmbox */
+      netconn_close_shutdown(conn, NETCONN_SHUT_RD);
+      /* Don' store ERR_CLSD as conn->err since we are only half-closed */
+      return ERR_CLSD;
+    }
+    len = ((struct pbuf *)buf)->tot_len;
+  }
+#endif /* LWIP_TCP */
+#if LWIP_TCP && (LWIP_UDP || LWIP_RAW)
+  else
+#endif /* LWIP_TCP && (LWIP_UDP || LWIP_RAW) */
+#if (LWIP_UDP || LWIP_RAW)
+  {
+    LWIP_ASSERT("buf != NULL", buf != NULL);
+    len = netbuf_len((struct netbuf *)buf);
+  }
+#endif /* (LWIP_UDP || LWIP_RAW) */
+
+#if LWIP_SO_RCVBUF
+  SYS_ARCH_DEC(conn->recv_avail, len);
+#endif /* LWIP_SO_RCVBUF */
+  /* Register event with callback */
+  API_EVENT(conn, NETCONN_EVT_RCVMINUS, len);
+
+  LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_recv_data: received %p, len=%"U16_F"\n", buf, len));
+
+  *new_buf = buf;
+  /* don't set conn->last_err: it's only ERR_OK, anyway */
+  return ERR_OK;
+}
+
+/**
+ * @ingroup netconn_tcp
+ * Receive data (in form of a pbuf) from a TCP netconn
+ *
+ * @param conn the netconn from which to receive data
+ * @param new_buf pointer where a new pbuf is stored when received data
+ * @return ERR_OK if data has been received, an error code otherwise (timeout,
+ *                memory error or another error)
+ *         ERR_ARG if conn is not a TCP netconn
+ */
+err_t
+netconn_recv_tcp_pbuf(struct netconn *conn, struct pbuf **new_buf)
+{
+  LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL) &&
+             NETCONNTYPE_GROUP(netconn_type(conn)) == NETCONN_TCP, return ERR_ARG;);
+
+  return netconn_recv_data(conn, (void **)new_buf);
+}
+
+/**
+ * @ingroup netconn_common
+ * Receive data (in form of a netbuf containing a packet buffer) from a netconn
+ *
+ * @param conn the netconn from which to receive data
+ * @param new_buf pointer where a new netbuf is stored when received data
+ * @return ERR_OK if data has been received, an error code otherwise (timeout,
+ *                memory error or another error)
+ */
+err_t
+netconn_recv(struct netconn *conn, struct netbuf **new_buf)
+{
+#if LWIP_TCP
+  struct netbuf *buf = NULL;
+  err_t err;
+#endif /* LWIP_TCP */
+
+  LWIP_ERROR("netconn_recv: invalid pointer", (new_buf != NULL), return ERR_ARG;);
+  *new_buf = NULL;
+  LWIP_ERROR("netconn_recv: invalid conn",    (conn != NULL),    return ERR_ARG;);
+
+#if LWIP_TCP
+#if (LWIP_UDP || LWIP_RAW)
+  if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP)
+#endif /* (LWIP_UDP || LWIP_RAW) */
+  {
+    struct pbuf *p = NULL;
+    /* This is not a listening netconn, since recvmbox is set */
+
+    buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
+    if (buf == NULL) {
+      return ERR_MEM;
+    }
+
+    err = netconn_recv_data(conn, (void **)&p);
+    if (err != ERR_OK) {
+      memp_free(MEMP_NETBUF, buf);
+      return err;
+    }
+    LWIP_ASSERT("p != NULL", p != NULL);
+
+    buf->p = p;
+    buf->ptr = p;
+    buf->port = 0;
+    ip_addr_set_zero(&buf->addr);
+    *new_buf = buf;
+    /* don't set conn->last_err: it's only ERR_OK, anyway */
+    return ERR_OK;
+  }
+#endif /* LWIP_TCP */
+#if LWIP_TCP && (LWIP_UDP || LWIP_RAW)
+  else
+#endif /* LWIP_TCP && (LWIP_UDP || LWIP_RAW) */
+  {
+#if (LWIP_UDP || LWIP_RAW)
+    return netconn_recv_data(conn, (void **)new_buf);
+#endif /* (LWIP_UDP || LWIP_RAW) */
+  }
+}
+
+/**
+ * @ingroup netconn_udp
+ * Send data (in form of a netbuf) to a specific remote IP address and port.
+ * Only to be used for UDP and RAW netconns (not TCP).
+ *
+ * @param conn the netconn over which to send data
+ * @param buf a netbuf containing the data to send
+ * @param addr the remote IP address to which to send the data
+ * @param port the remote port to which to send the data
+ * @return ERR_OK if data was sent, any other err_t on error
+ */
+err_t
+netconn_sendto(struct netconn *conn, struct netbuf *buf, const ip_addr_t *addr, u16_t port)
+{
+  if (buf != NULL) {
+    ip_addr_set(&buf->addr, addr);
+    buf->port = port;
+    return netconn_send(conn, buf);
+  }
+  return ERR_VAL;
+}
+
+/**
+ * @ingroup netconn_udp
+ * Send data over a UDP or RAW netconn (that is already connected).
+ *
+ * @param conn the UDP or RAW netconn over which to send data
+ * @param buf a netbuf containing the data to send
+ * @return ERR_OK if data was sent, any other err_t on error
+ */
+err_t
+netconn_send(struct netconn *conn, struct netbuf *buf)
+{
+  API_MSG_VAR_DECLARE(msg);
+  err_t err;
+
+  LWIP_ERROR("netconn_send: invalid conn",  (conn != NULL), return ERR_ARG;);
+
+  LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_send: sending %"U16_F" bytes\n", buf->p->tot_len));
+  API_MSG_VAR_ALLOC(msg);
+  API_MSG_VAR_REF(msg).conn = conn;
+  API_MSG_VAR_REF(msg).msg.b = buf;
+  err = netconn_apimsg(lwip_netconn_do_send, &API_MSG_VAR_REF(msg));
+  API_MSG_VAR_FREE(msg);
+
+  return err;
+}
+
+/**
+ * @ingroup netconn_tcp
+ * Send data over a TCP netconn.
+ *
+ * @param conn the TCP netconn over which to send data
+ * @param dataptr pointer to the application buffer that contains the data to send
+ * @param size size of the application data to send
+ * @param apiflags combination of following flags :
+ * - NETCONN_COPY: data will be copied into memory belonging to the stack
+ * - NETCONN_MORE: for TCP connection, PSH flag will be set on last segment sent
+ * - NETCONN_DONTBLOCK: only write the data if all data can be written at once
+ * @param bytes_written pointer to a location that receives the number of written bytes
+ * @return ERR_OK if data was sent, any other err_t on error
+ */
+err_t
+netconn_write_partly(struct netconn *conn, const void *dataptr, size_t size,
+                     u8_t apiflags, size_t *bytes_written)
+{
+  API_MSG_VAR_DECLARE(msg);
+  err_t err;
+  u8_t dontblock;
+
+  LWIP_ERROR("netconn_write: invalid conn",  (conn != NULL), return ERR_ARG;);
+  LWIP_ERROR("netconn_write: invalid conn->type",  (NETCONNTYPE_GROUP(conn->type)== NETCONN_TCP), return ERR_VAL;);
+  if (size == 0) {
+    return ERR_OK;
+  }
+  dontblock = netconn_is_nonblocking(conn) || (apiflags & NETCONN_DONTBLOCK);
+  if (dontblock && !bytes_written) {
+    /* This implies netconn_write() cannot be used for non-blocking send, since
+       it has no way to return the number of bytes written. */
+    return ERR_VAL;
+  }
+
+  API_MSG_VAR_ALLOC(msg);
+  /* non-blocking write sends as much  */
+  API_MSG_VAR_REF(msg).conn = conn;
+  API_MSG_VAR_REF(msg).msg.w.dataptr = dataptr;
+  API_MSG_VAR_REF(msg).msg.w.apiflags = apiflags;
+  API_MSG_VAR_REF(msg).msg.w.len = size;
+#if LWIP_SO_SNDTIMEO
+  if (conn->send_timeout != 0) {
+    /* get the time we started, which is later compared to
+        sys_now() + conn->send_timeout */
+    API_MSG_VAR_REF(msg).msg.w.time_started = sys_now();
+  } else {
+    API_MSG_VAR_REF(msg).msg.w.time_started = 0;
+  }
+#endif /* LWIP_SO_SNDTIMEO */
+
+  /* For locking the core: this _can_ be delayed on low memory/low send buffer,
+     but if it is, this is done inside api_msg.c:do_write(), so we can use the
+     non-blocking version here. */
+  err = netconn_apimsg(lwip_netconn_do_write, &API_MSG_VAR_REF(msg));
+  if ((err == ERR_OK) && (bytes_written != NULL)) {
+    if (dontblock
+#if LWIP_SO_SNDTIMEO
+        || (conn->send_timeout != 0)
+#endif /* LWIP_SO_SNDTIMEO */
+       ) {
+      /* nonblocking write: maybe the data has been sent partly */
+      *bytes_written = API_MSG_VAR_REF(msg).msg.w.len;
+    } else {
+      /* blocking call succeeded: all data has been sent if it */
+      *bytes_written = size;
+    }
+  }
+  API_MSG_VAR_FREE(msg);
+
+  return err;
+}
+
+/**
+ * @ingroup netconn_tcp
+ * Close or shutdown a TCP netconn (doesn't delete it).
+ *
+ * @param conn the TCP netconn to close or shutdown
+ * @param how fully close or only shutdown one side?
+ * @return ERR_OK if the netconn was closed, any other err_t on error
+ */
+static err_t
+netconn_close_shutdown(struct netconn *conn, u8_t how)
+{
+  API_MSG_VAR_DECLARE(msg);
+  err_t err;
+  LWIP_UNUSED_ARG(how);
+
+  LWIP_ERROR("netconn_close: invalid conn",  (conn != NULL), return ERR_ARG;);
+
+  API_MSG_VAR_ALLOC(msg);
+  API_MSG_VAR_REF(msg).conn = conn;
+#if LWIP_TCP
+  /* shutting down both ends is the same as closing */
+  API_MSG_VAR_REF(msg).msg.sd.shut = how;
+#if LWIP_SO_SNDTIMEO || LWIP_SO_LINGER
+  /* get the time we started, which is later compared to
+     sys_now() + conn->send_timeout */
+  API_MSG_VAR_REF(msg).msg.sd.time_started = sys_now();
+#else /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
+  API_MSG_VAR_REF(msg).msg.sd.polls_left =
+    ((LWIP_TCP_CLOSE_TIMEOUT_MS_DEFAULT + TCP_SLOW_INTERVAL - 1) / TCP_SLOW_INTERVAL) + 1;
+#endif /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
+#endif /* LWIP_TCP */
+  err = netconn_apimsg(lwip_netconn_do_close, &API_MSG_VAR_REF(msg));
+  API_MSG_VAR_FREE(msg);
+
+  return err;
+}
+
+/**
+ * @ingroup netconn_tcp
+ * Close a TCP netconn (doesn't delete it).
+ *
+ * @param conn the TCP netconn to close
+ * @return ERR_OK if the netconn was closed, any other err_t on error
+ */
+err_t
+netconn_close(struct netconn *conn)
+{
+  /* shutting down both ends is the same as closing */
+  return netconn_close_shutdown(conn, NETCONN_SHUT_RDWR);
+}
+
+/**
+ * @ingroup netconn_tcp
+ * Shut down one or both sides of a TCP netconn (doesn't delete it).
+ *
+ * @param conn the TCP netconn to shut down
+ * @param shut_rx shut down the RX side (no more read possible after this)
+ * @param shut_tx shut down the TX side (no more write possible after this)
+ * @return ERR_OK if the netconn was closed, any other err_t on error
+ */
+err_t
+netconn_shutdown(struct netconn *conn, u8_t shut_rx, u8_t shut_tx)
+{
+  return netconn_close_shutdown(conn, (shut_rx ? NETCONN_SHUT_RD : 0) | (shut_tx ? NETCONN_SHUT_WR : 0));
+}
+
+#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD)
+/**
+ * @ingroup netconn_udp
+ * Join multicast groups for UDP netconns.
+ *
+ * @param conn the UDP netconn for which to change multicast addresses
+ * @param multiaddr IP address of the multicast group to join or leave
+ * @param netif_addr the IP address of the network interface on which to send
+ *                  the igmp message
+ * @param join_or_leave flag whether to send a join- or leave-message
+ * @return ERR_OK if the action was taken, any err_t on error
+ */
+err_t
+netconn_join_leave_group(struct netconn *conn,
+                         const ip_addr_t *multiaddr,
+                         const ip_addr_t *netif_addr,
+                         enum netconn_igmp join_or_leave)
+{
+  API_MSG_VAR_DECLARE(msg);
+  err_t err;
+
+  LWIP_ERROR("netconn_join_leave_group: invalid conn",  (conn != NULL), return ERR_ARG;);
+
+  API_MSG_VAR_ALLOC(msg);
+
+  /* Don't propagate NULL pointer (IP_ADDR_ANY alias) to subsequent functions */
+  if (multiaddr == NULL) {
+    multiaddr = IP4_ADDR_ANY;
+  }
+  if (netif_addr == NULL) {
+    netif_addr = IP4_ADDR_ANY;
+  }
+
+  API_MSG_VAR_REF(msg).conn = conn;
+  API_MSG_VAR_REF(msg).msg.jl.multiaddr = API_MSG_VAR_REF(multiaddr);
+  API_MSG_VAR_REF(msg).msg.jl.netif_addr = API_MSG_VAR_REF(netif_addr);
+  API_MSG_VAR_REF(msg).msg.jl.join_or_leave = join_or_leave;
+  err = netconn_apimsg(lwip_netconn_do_join_leave_group, &API_MSG_VAR_REF(msg));
+  API_MSG_VAR_FREE(msg);
+
+  return err;
+}
+#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */
+
+#if LWIP_DNS
+/**
+ * @ingroup netconn_common
+ * Execute a DNS query, only one IP address is returned
+ *
+ * @param name a string representation of the DNS host name to query
+ * @param addr a preallocated ip_addr_t where to store the resolved IP address
+ * @param dns_addrtype IP address type (IPv4 / IPv6)
+ * @return ERR_OK: resolving succeeded
+ *         ERR_MEM: memory error, try again later
+ *         ERR_ARG: dns client not initialized or invalid hostname
+ *         ERR_VAL: dns server response was invalid
+ */
+#if LWIP_IPV4 && LWIP_IPV6
+err_t
+netconn_gethostbyname_addrtype(const char *name, ip_addr_t *addr, u8_t dns_addrtype)
+#else
+err_t
+netconn_gethostbyname(const char *name, ip_addr_t *addr)
+#endif
+{
+  API_VAR_DECLARE(struct dns_api_msg, msg);
+#if !LWIP_MPU_COMPATIBLE
+  sys_sem_t sem;
+#endif /* LWIP_MPU_COMPATIBLE */
+  err_t err;
+
+  LWIP_ERROR("netconn_gethostbyname: invalid name", (name != NULL), return ERR_ARG;);
+  LWIP_ERROR("netconn_gethostbyname: invalid addr", (addr != NULL), return ERR_ARG;);
+#if LWIP_MPU_COMPATIBLE
+  if (strlen(name) >= DNS_MAX_NAME_LENGTH) {
+    return ERR_ARG;
+  }
+#endif
+
+  API_VAR_ALLOC(struct dns_api_msg, MEMP_DNS_API_MSG, msg, ERR_MEM);
+#if LWIP_MPU_COMPATIBLE
+  strncpy(API_VAR_REF(msg).name, name, DNS_MAX_NAME_LENGTH-1);
+  API_VAR_REF(msg).name[DNS_MAX_NAME_LENGTH-1] = 0;
+#else /* LWIP_MPU_COMPATIBLE */
+  msg.err = &err;
+  msg.sem = &sem;
+  API_VAR_REF(msg).addr = API_VAR_REF(addr);
+  API_VAR_REF(msg).name = name;
+#endif /* LWIP_MPU_COMPATIBLE */
+#if LWIP_IPV4 && LWIP_IPV6
+  API_VAR_REF(msg).dns_addrtype = dns_addrtype;
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+#if LWIP_NETCONN_SEM_PER_THREAD
+  API_VAR_REF(msg).sem = LWIP_NETCONN_THREAD_SEM_GET();
+#else /* LWIP_NETCONN_SEM_PER_THREAD*/
+  err = sys_sem_new(API_EXPR_REF(API_VAR_REF(msg).sem), 0);
+  if (err != ERR_OK) {
+    API_VAR_FREE(MEMP_DNS_API_MSG, msg);
+    return err;
+  }
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+  err = tcpip_callback(lwip_netconn_do_gethostbyname, &API_VAR_REF(msg));
+  if (err != ERR_OK) {
+#if !LWIP_NETCONN_SEM_PER_THREAD
+    sys_sem_free(API_EXPR_REF(API_VAR_REF(msg).sem));
+#endif /* !LWIP_NETCONN_SEM_PER_THREAD */
+    API_VAR_FREE(MEMP_DNS_API_MSG, msg);
+    return err;
+  }
+  sys_sem_wait(API_EXPR_REF_SEM(API_VAR_REF(msg).sem));
+#if !LWIP_NETCONN_SEM_PER_THREAD
+  sys_sem_free(API_EXPR_REF(API_VAR_REF(msg).sem));
+#endif /* !LWIP_NETCONN_SEM_PER_THREAD */
+
+#if LWIP_MPU_COMPATIBLE
+  *addr = msg->addr;
+  err = msg->err;
+#endif /* LWIP_MPU_COMPATIBLE */
+
+  API_VAR_FREE(MEMP_DNS_API_MSG, msg);
+  return err;
+}
+#endif /* LWIP_DNS*/
+
+#if LWIP_NETCONN_SEM_PER_THREAD
+void
+netconn_thread_init(void)
+{
+  sys_sem_t *sem = LWIP_NETCONN_THREAD_SEM_GET();
+  if ((sem == NULL) || !sys_sem_valid(sem)) {
+    /* call alloc only once */
+    LWIP_NETCONN_THREAD_SEM_ALLOC();
+    LWIP_ASSERT("LWIP_NETCONN_THREAD_SEM_ALLOC() failed", sys_sem_valid(LWIP_NETCONN_THREAD_SEM_GET()));
+  }
+}
+
+void
+netconn_thread_cleanup(void)
+{
+  sys_sem_t *sem = LWIP_NETCONN_THREAD_SEM_GET();
+  if ((sem != NULL) && sys_sem_valid(sem)) {
+    /* call free only once */
+    LWIP_NETCONN_THREAD_SEM_FREE();
+  }
+}
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+#endif /* LWIP_NETCONN */

+ 1947 - 2173
libs/thirdparty/lwip_2.1.2/src/api/api_msg.c → libs/thirdparty/LwIP/src/api/api_msg.c

@@ -1,2173 +1,1947 @@
-/**
- * @file
- * Sequential API Internal module
- *
- */
-
-/*
- * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- *
- * Author: Adam Dunkels <adam@sics.se>
- *
- */
-
-#include "lwip/opt.h"
-
-#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
-
-#include "lwip/priv/api_msg.h"
-
-#include "lwip/ip.h"
-#include "lwip/ip_addr.h"
-#include "lwip/udp.h"
-#include "lwip/tcp.h"
-#include "lwip/raw.h"
-
-#include "lwip/memp.h"
-#include "lwip/igmp.h"
-#include "lwip/dns.h"
-#include "lwip/mld6.h"
-#include "lwip/priv/tcpip_priv.h"
-
-#include <string.h>
-
-/* netconns are polled once per second (e.g. continue write on memory error) */
-#define NETCONN_TCP_POLL_INTERVAL 2
-
-#define SET_NONBLOCKING_CONNECT(conn, val)  do { if (val) { \
-  netconn_set_flags(conn, NETCONN_FLAG_IN_NONBLOCKING_CONNECT); \
-} else { \
-  netconn_clear_flags(conn, NETCONN_FLAG_IN_NONBLOCKING_CONNECT); }} while(0)
-#define IN_NONBLOCKING_CONNECT(conn) netconn_is_flag_set(conn, NETCONN_FLAG_IN_NONBLOCKING_CONNECT)
-
-#if LWIP_NETCONN_FULLDUPLEX
-#define NETCONN_MBOX_VALID(conn, mbox) (sys_mbox_valid(mbox) && ((conn->flags & NETCONN_FLAG_MBOXINVALID) == 0))
-#else
-#define NETCONN_MBOX_VALID(conn, mbox) sys_mbox_valid(mbox)
-#endif
-
-/* forward declarations */
-#if LWIP_TCP
-#if LWIP_TCPIP_CORE_LOCKING
-#define WRITE_DELAYED         , 1
-#define WRITE_DELAYED_PARAM   , u8_t delayed
-#else /* LWIP_TCPIP_CORE_LOCKING */
-#define WRITE_DELAYED
-#define WRITE_DELAYED_PARAM
-#endif /* LWIP_TCPIP_CORE_LOCKING */
-static err_t lwip_netconn_do_writemore(struct netconn *conn  WRITE_DELAYED_PARAM);
-static err_t lwip_netconn_do_close_internal(struct netconn *conn  WRITE_DELAYED_PARAM);
-#endif
-
-static void netconn_drain(struct netconn *conn);
-
-#if LWIP_TCPIP_CORE_LOCKING
-#define TCPIP_APIMSG_ACK(m)
-#else /* LWIP_TCPIP_CORE_LOCKING */
-#define TCPIP_APIMSG_ACK(m)   do { sys_sem_signal(LWIP_API_MSG_SEM(m)); } while(0)
-#endif /* LWIP_TCPIP_CORE_LOCKING */
-
-#if LWIP_NETCONN_FULLDUPLEX
-const u8_t netconn_deleted = 0;
-
-int
-lwip_netconn_is_deallocated_msg(void *msg)
-{
-  if (msg == &netconn_deleted) {
-    return 1;
-  }
-  return 0;
-}
-#endif /* LWIP_NETCONN_FULLDUPLEX */
-
-#if LWIP_TCP
-const u8_t netconn_aborted = 0;
-const u8_t netconn_reset = 0;
-const u8_t netconn_closed = 0;
-
-/** Translate an error to a unique void* passed via an mbox */
-static void *
-lwip_netconn_err_to_msg(err_t err)
-{
-  switch (err) {
-    case ERR_ABRT:
-      return LWIP_CONST_CAST(void *, &netconn_aborted);
-    case ERR_RST:
-      return LWIP_CONST_CAST(void *, &netconn_reset);
-    case ERR_CLSD:
-      return LWIP_CONST_CAST(void *, &netconn_closed);
-    default:
-      LWIP_ASSERT("unhandled error", err == ERR_OK);
-      return NULL;
-  }
-}
-
-int
-lwip_netconn_is_err_msg(void *msg, err_t *err)
-{
-  LWIP_ASSERT("err != NULL", err != NULL);
-
-  if (msg == &netconn_aborted) {
-    *err = ERR_ABRT;
-    return 1;
-  } else if (msg == &netconn_reset) {
-    *err = ERR_RST;
-    return 1;
-  } else if (msg == &netconn_closed) {
-    *err = ERR_CLSD;
-    return 1;
-  }
-  return 0;
-}
-#endif /* LWIP_TCP */
-
-
-#if LWIP_RAW
-/**
- * Receive callback function for RAW netconns.
- * Doesn't 'eat' the packet, only copies it and sends it to
- * conn->recvmbox
- *
- * @see raw.h (struct raw_pcb.recv) for parameters and return value
- */
-static u8_t
-recv_raw(void *arg, struct raw_pcb *pcb, struct pbuf *p,
-         const ip_addr_t *addr)
-{
-  struct pbuf *q;
-  struct netbuf *buf;
-  struct netconn *conn;
-
-  LWIP_UNUSED_ARG(addr);
-  conn = (struct netconn *)arg;
-
-  if ((conn != NULL) && NETCONN_MBOX_VALID(conn, &conn->recvmbox)) {
-#if LWIP_SO_RCVBUF
-    int recv_avail;
-    SYS_ARCH_GET(conn->recv_avail, recv_avail);
-    if ((recv_avail + (int)(p->tot_len)) > conn->recv_bufsize) {
-      return 0;
-    }
-#endif /* LWIP_SO_RCVBUF */
-    /* copy the whole packet into new pbufs */
-    q = pbuf_clone(PBUF_RAW, PBUF_RAM, p);
-    if (q != NULL) {
-      u16_t len;
-      buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
-      if (buf == NULL) {
-        pbuf_free(q);
-        return 0;
-      }
-
-      buf->p = q;
-      buf->ptr = q;
-      ip_addr_copy(buf->addr, *ip_current_src_addr());
-      buf->port = pcb->protocol;
-
-      len = q->tot_len;
-      if (sys_mbox_trypost(&conn->recvmbox, buf) != ERR_OK) {
-        netbuf_delete(buf);
-        return 0;
-      } else {
-#if LWIP_SO_RCVBUF
-        SYS_ARCH_INC(conn->recv_avail, len);
-#endif /* LWIP_SO_RCVBUF */
-        /* Register event with callback */
-        API_EVENT(conn, NETCONN_EVT_RCVPLUS, len);
-      }
-    }
-  }
-
-  return 0; /* do not eat the packet */
-}
-#endif /* LWIP_RAW*/
-
-#if LWIP_UDP
-/**
- * Receive callback function for UDP netconns.
- * Posts the packet to conn->recvmbox or deletes it on memory error.
- *
- * @see udp.h (struct udp_pcb.recv) for parameters
- */
-static void
-recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p,
-         const ip_addr_t *addr, u16_t port)
-{
-  struct netbuf *buf;
-  struct netconn *conn;
-  u16_t len;
-#if LWIP_SO_RCVBUF
-  int recv_avail;
-#endif /* LWIP_SO_RCVBUF */
-
-  LWIP_UNUSED_ARG(pcb); /* only used for asserts... */
-  LWIP_ASSERT("recv_udp must have a pcb argument", pcb != NULL);
-  LWIP_ASSERT("recv_udp must have an argument", arg != NULL);
-  conn = (struct netconn *)arg;
-
-  if (conn == NULL) {
-    pbuf_free(p);
-    return;
-  }
-
-  LWIP_ASSERT("recv_udp: recv for wrong pcb!", conn->pcb.udp == pcb);
-
-#if LWIP_SO_RCVBUF
-  SYS_ARCH_GET(conn->recv_avail, recv_avail);
-  if (!NETCONN_MBOX_VALID(conn, &conn->recvmbox) ||
-      ((recv_avail + (int)(p->tot_len)) > conn->recv_bufsize)) {
-#else  /* LWIP_SO_RCVBUF */
-  if (!NETCONN_MBOX_VALID(conn, &conn->recvmbox)) {
-#endif /* LWIP_SO_RCVBUF */
-    pbuf_free(p);
-    return;
-  }
-
-  buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
-  if (buf == NULL) {
-    pbuf_free(p);
-    return;
-  } else {
-    buf->p = p;
-    buf->ptr = p;
-    ip_addr_set(&buf->addr, addr);
-    buf->port = port;
-#if LWIP_NETBUF_RECVINFO
-    if (conn->flags & NETCONN_FLAG_PKTINFO) {
-      /* get the UDP header - always in the first pbuf, ensured by udp_input */
-      const struct udp_hdr *udphdr = (const struct udp_hdr *)ip_next_header_ptr();
-      buf->flags = NETBUF_FLAG_DESTADDR;
-      ip_addr_set(&buf->toaddr, ip_current_dest_addr());
-      buf->toport_chksum = udphdr->dest;
-    }
-#endif /* LWIP_NETBUF_RECVINFO */
-  }
-
-  len = p->tot_len;
-  if (sys_mbox_trypost(&conn->recvmbox, buf) != ERR_OK) {
-    netbuf_delete(buf);
-    return;
-  } else {
-#if LWIP_SO_RCVBUF
-    SYS_ARCH_INC(conn->recv_avail, len);
-#endif /* LWIP_SO_RCVBUF */
-    /* Register event with callback */
-    API_EVENT(conn, NETCONN_EVT_RCVPLUS, len);
-  }
-}
-#endif /* LWIP_UDP */
-
-#if LWIP_TCP
-/**
- * Receive callback function for TCP netconns.
- * Posts the packet to conn->recvmbox, but doesn't delete it on errors.
- *
- * @see tcp.h (struct tcp_pcb.recv) for parameters and return value
- */
-static err_t
-recv_tcp(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
-{
-  struct netconn *conn;
-  u16_t len;
-  void *msg;
-
-  LWIP_UNUSED_ARG(pcb);
-  LWIP_ASSERT("recv_tcp must have a pcb argument", pcb != NULL);
-  LWIP_ASSERT("recv_tcp must have an argument", arg != NULL);
-  LWIP_ASSERT("err != ERR_OK unhandled", err == ERR_OK);
-  LWIP_UNUSED_ARG(err); /* for LWIP_NOASSERT */
-  conn = (struct netconn *)arg;
-
-  if (conn == NULL) {
-    return ERR_VAL;
-  }
-  LWIP_ASSERT("recv_tcp: recv for wrong pcb!", conn->pcb.tcp == pcb);
-
-  if (!NETCONN_MBOX_VALID(conn, &conn->recvmbox)) {
-    /* recvmbox already deleted */
-    if (p != NULL) {
-      tcp_recved(pcb, p->tot_len);
-      pbuf_free(p);
-    }
-    return ERR_OK;
-  }
-  /* Unlike for UDP or RAW pcbs, don't check for available space
-     using recv_avail since that could break the connection
-     (data is already ACKed) */
-
-  if (p != NULL) {
-    msg = p;
-    len = p->tot_len;
-  } else {
-    msg = LWIP_CONST_CAST(void *, &netconn_closed);
-    len = 0;
-  }
-
-  if (sys_mbox_trypost(&conn->recvmbox, msg) != ERR_OK) {
-    /* don't deallocate p: it is presented to us later again from tcp_fasttmr! */
-    return ERR_MEM;
-  } else {
-#if LWIP_SO_RCVBUF
-    SYS_ARCH_INC(conn->recv_avail, len);
-#endif /* LWIP_SO_RCVBUF */
-    /* Register event with callback */
-    API_EVENT(conn, NETCONN_EVT_RCVPLUS, len);
-  }
-
-  return ERR_OK;
-}
-
-/**
- * Poll callback function for TCP netconns.
- * Wakes up an application thread that waits for a connection to close
- * or data to be sent. The application thread then takes the
- * appropriate action to go on.
- *
- * Signals the conn->sem.
- * netconn_close waits for conn->sem if closing failed.
- *
- * @see tcp.h (struct tcp_pcb.poll) for parameters and return value
- */
-static err_t
-poll_tcp(void *arg, struct tcp_pcb *pcb)
-{
-  struct netconn *conn = (struct netconn *)arg;
-
-  LWIP_UNUSED_ARG(pcb);
-  LWIP_ASSERT("conn != NULL", (conn != NULL));
-
-  if (conn->state == NETCONN_WRITE) {
-    lwip_netconn_do_writemore(conn  WRITE_DELAYED);
-  } else if (conn->state == NETCONN_CLOSE) {
-#if !LWIP_SO_SNDTIMEO && !LWIP_SO_LINGER
-    if (conn->current_msg && conn->current_msg->msg.sd.polls_left) {
-      conn->current_msg->msg.sd.polls_left--;
-    }
-#endif /* !LWIP_SO_SNDTIMEO && !LWIP_SO_LINGER */
-    lwip_netconn_do_close_internal(conn  WRITE_DELAYED);
-  }
-  /* @todo: implement connect timeout here? */
-
-  /* Did a nonblocking write fail before? Then check available write-space. */
-  if (conn->flags & NETCONN_FLAG_CHECK_WRITESPACE) {
-    /* If the queued byte- or pbuf-count drops below the configured low-water limit,
-       let select mark this pcb as writable again. */
-    if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT) &&
-        (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT)) {
-      netconn_clear_flags(conn, NETCONN_FLAG_CHECK_WRITESPACE);
-      API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
-    }
-  }
-
-  return ERR_OK;
-}
-
-/**
- * Sent callback function for TCP netconns.
- * Signals the conn->sem and calls API_EVENT.
- * netconn_write waits for conn->sem if send buffer is low.
- *
- * @see tcp.h (struct tcp_pcb.sent) for parameters and return value
- */
-static err_t
-sent_tcp(void *arg, struct tcp_pcb *pcb, u16_t len)
-{
-  struct netconn *conn = (struct netconn *)arg;
-
-  LWIP_UNUSED_ARG(pcb);
-  LWIP_ASSERT("conn != NULL", (conn != NULL));
-
-  if (conn) {
-    if (conn->state == NETCONN_WRITE) {
-      lwip_netconn_do_writemore(conn  WRITE_DELAYED);
-    } else if (conn->state == NETCONN_CLOSE) {
-      lwip_netconn_do_close_internal(conn  WRITE_DELAYED);
-    }
-
-    /* If the queued byte- or pbuf-count drops below the configured low-water limit,
-       let select mark this pcb as writable again. */
-    if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT) &&
-        (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT)) {
-      netconn_clear_flags(conn, NETCONN_FLAG_CHECK_WRITESPACE);
-      API_EVENT(conn, NETCONN_EVT_SENDPLUS, len);
-    }
-  }
-
-  return ERR_OK;
-}
-
-/**
- * Error callback function for TCP netconns.
- * Signals conn->sem, posts to all conn mboxes and calls API_EVENT.
- * The application thread has then to decide what to do.
- *
- * @see tcp.h (struct tcp_pcb.err) for parameters
- */
-static void
-err_tcp(void *arg, err_t err)
-{
-  struct netconn *conn;
-  enum netconn_state old_state;
-  void *mbox_msg;
-  SYS_ARCH_DECL_PROTECT(lev);
-
-  conn = (struct netconn *)arg;
-  LWIP_ASSERT("conn != NULL", (conn != NULL));
-
-  SYS_ARCH_PROTECT(lev);
-
-  /* when err is called, the pcb is deallocated, so delete the reference */
-  conn->pcb.tcp = NULL;
-  /* store pending error */
-  conn->pending_err = err;
-  /* prevent application threads from blocking on 'recvmbox'/'acceptmbox' */
-  conn->flags |= NETCONN_FLAG_MBOXCLOSED;
-
-  /* reset conn->state now before waking up other threads */
-  old_state = conn->state;
-  conn->state = NETCONN_NONE;
-
-  SYS_ARCH_UNPROTECT(lev);
-
-  /* Notify the user layer about a connection error. Used to signal select. */
-  API_EVENT(conn, NETCONN_EVT_ERROR, 0);
-  /* Try to release selects pending on 'read' or 'write', too.
-     They will get an error if they actually try to read or write. */
-  API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
-  API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
-
-  mbox_msg = lwip_netconn_err_to_msg(err);
-  /* pass error message to recvmbox to wake up pending recv */
-  if (NETCONN_MBOX_VALID(conn, &conn->recvmbox)) {
-    /* use trypost to prevent deadlock */
-    sys_mbox_trypost(&conn->recvmbox, mbox_msg);
-  }
-  /* pass error message to acceptmbox to wake up pending accept */
-  if (NETCONN_MBOX_VALID(conn, &conn->acceptmbox)) {
-    /* use trypost to preven deadlock */
-    sys_mbox_trypost(&conn->acceptmbox, mbox_msg);
-  }
-
-  if ((old_state == NETCONN_WRITE) || (old_state == NETCONN_CLOSE) ||
-      (old_state == NETCONN_CONNECT)) {
-    /* calling lwip_netconn_do_writemore/lwip_netconn_do_close_internal is not necessary
-       since the pcb has already been deleted! */
-    int was_nonblocking_connect = IN_NONBLOCKING_CONNECT(conn);
-    SET_NONBLOCKING_CONNECT(conn, 0);
-
-    if (!was_nonblocking_connect) {
-      sys_sem_t *op_completed_sem;
-      /* set error return code */
-      LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
-      if (old_state == NETCONN_CLOSE) {
-        /* let close succeed: the connection is closed after all... */
-        conn->current_msg->err = ERR_OK;
-      } else {
-        /* Write and connect fail */
-        conn->current_msg->err = err;
-      }
-      op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
-      LWIP_ASSERT("inavlid op_completed_sem", sys_sem_valid(op_completed_sem));
-      conn->current_msg = NULL;
-      /* wake up the waiting task */
-      sys_sem_signal(op_completed_sem);
-    } else {
-      /* @todo: test what happens for error on nonblocking connect */
-    }
-  } else {
-    LWIP_ASSERT("conn->current_msg == NULL", conn->current_msg == NULL);
-  }
-}
-
-/**
- * Setup a tcp_pcb with the correct callback function pointers
- * and their arguments.
- *
- * @param conn the TCP netconn to setup
- */
-static void
-setup_tcp(struct netconn *conn)
-{
-  struct tcp_pcb *pcb;
-
-  pcb = conn->pcb.tcp;
-  tcp_arg(pcb, conn);
-  tcp_recv(pcb, recv_tcp);
-  tcp_sent(pcb, sent_tcp);
-  tcp_poll(pcb, poll_tcp, NETCONN_TCP_POLL_INTERVAL);
-  tcp_err(pcb, err_tcp);
-}
-
-/**
- * Accept callback function for TCP netconns.
- * Allocates a new netconn and posts that to conn->acceptmbox.
- *
- * @see tcp.h (struct tcp_pcb_listen.accept) for parameters and return value
- */
-static err_t
-accept_function(void *arg, struct tcp_pcb *newpcb, err_t err)
-{
-  struct netconn *newconn;
-  struct netconn *conn = (struct netconn *)arg;
-
-  if (conn == NULL) {
-    return ERR_VAL;
-  }
-  if (!NETCONN_MBOX_VALID(conn, &conn->acceptmbox)) {
-    LWIP_DEBUGF(API_MSG_DEBUG, ("accept_function: acceptmbox already deleted\n"));
-    return ERR_VAL;
-  }
-
-  if (newpcb == NULL) {
-    /* out-of-pcbs during connect: pass on this error to the application */
-    if (sys_mbox_trypost(&conn->acceptmbox, lwip_netconn_err_to_msg(ERR_ABRT)) == ERR_OK) {
-      /* Register event with callback */
-      API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
-    }
-    return ERR_VAL;
-  }
-  LWIP_ASSERT("expect newpcb == NULL or err == ERR_OK", err == ERR_OK);
-  LWIP_UNUSED_ARG(err); /* for LWIP_NOASSERT */
-
-  LWIP_DEBUGF(API_MSG_DEBUG, ("accept_function: newpcb->state: %s\n", tcp_debug_state_str(newpcb->state)));
-
-  /* We have to set the callback here even though
-   * the new socket is unknown. newconn->socket is marked as -1. */
-  newconn = netconn_alloc(conn->type, conn->callback);
-  if (newconn == NULL) {
-    /* outof netconns: pass on this error to the application */
-    if (sys_mbox_trypost(&conn->acceptmbox, lwip_netconn_err_to_msg(ERR_ABRT)) == ERR_OK) {
-      /* Register event with callback */
-      API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
-    }
-    return ERR_MEM;
-  }
-  newconn->pcb.tcp = newpcb;
-  setup_tcp(newconn);
-
-  /* handle backlog counter */
-  tcp_backlog_delayed(newpcb);
-
-  if (sys_mbox_trypost(&conn->acceptmbox, newconn) != ERR_OK) {
-    /* When returning != ERR_OK, the pcb is aborted in tcp_process(),
-       so do nothing here! */
-    /* remove all references to this netconn from the pcb */
-    struct tcp_pcb *pcb = newconn->pcb.tcp;
-    tcp_arg(pcb, NULL);
-    tcp_recv(pcb, NULL);
-    tcp_sent(pcb, NULL);
-    tcp_poll(pcb, NULL, 0);
-    tcp_err(pcb, NULL);
-    /* remove reference from to the pcb from this netconn */
-    newconn->pcb.tcp = NULL;
-    /* no need to drain since we know the recvmbox is empty. */
-    sys_mbox_free(&newconn->recvmbox);
-    sys_mbox_set_invalid(&newconn->recvmbox);
-    netconn_free(newconn);
-    return ERR_MEM;
-  } else {
-    /* Register event with callback */
-    API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
-  }
-
-  return ERR_OK;
-}
-#endif /* LWIP_TCP */
-
-/**
- * Create a new pcb of a specific type.
- * Called from lwip_netconn_do_newconn().
- *
- * @param msg the api_msg describing the connection type
- */
-static void
-pcb_new(struct api_msg *msg)
-{
-  enum lwip_ip_addr_type iptype = IPADDR_TYPE_V4;
-
-  LWIP_ASSERT("pcb_new: pcb already allocated", msg->conn->pcb.tcp == NULL);
-
-#if LWIP_IPV6 && LWIP_IPV4
-  /* IPv6: Dual-stack by default, unless netconn_set_ipv6only() is called */
-  if (NETCONNTYPE_ISIPV6(netconn_type(msg->conn))) {
-    iptype = IPADDR_TYPE_ANY;
-  }
-#endif
-
-  /* Allocate a PCB for this connection */
-  switch (NETCONNTYPE_GROUP(msg->conn->type)) {
-#if LWIP_RAW
-    case NETCONN_RAW:
-      msg->conn->pcb.raw = raw_new_ip_type(iptype, msg->msg.n.proto);
-      if (msg->conn->pcb.raw != NULL) {
-#if LWIP_IPV6
-        /* ICMPv6 packets should always have checksum calculated by the stack as per RFC 3542 chapter 3.1 */
-        if (NETCONNTYPE_ISIPV6(msg->conn->type) && msg->conn->pcb.raw->protocol == IP6_NEXTH_ICMP6) {
-          msg->conn->pcb.raw->chksum_reqd = 1;
-          msg->conn->pcb.raw->chksum_offset = 2;
-        }
-#endif /* LWIP_IPV6 */
-        raw_recv(msg->conn->pcb.raw, recv_raw, msg->conn);
-      }
-      break;
-#endif /* LWIP_RAW */
-#if LWIP_UDP
-    case NETCONN_UDP:
-      msg->conn->pcb.udp = udp_new_ip_type(iptype);
-      if (msg->conn->pcb.udp != NULL) {
-#if LWIP_UDPLITE
-        if (NETCONNTYPE_ISUDPLITE(msg->conn->type)) {
-          udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_UDPLITE);
-        }
-#endif /* LWIP_UDPLITE */
-        if (NETCONNTYPE_ISUDPNOCHKSUM(msg->conn->type)) {
-          udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_NOCHKSUM);
-        }
-        udp_recv(msg->conn->pcb.udp, recv_udp, msg->conn);
-      }
-      break;
-#endif /* LWIP_UDP */
-#if LWIP_TCP
-    case NETCONN_TCP:
-      msg->conn->pcb.tcp = tcp_new_ip_type(iptype);
-      if (msg->conn->pcb.tcp != NULL) {
-        setup_tcp(msg->conn);
-      }
-      break;
-#endif /* LWIP_TCP */
-    default:
-      /* Unsupported netconn type, e.g. protocol disabled */
-      msg->err = ERR_VAL;
-      return;
-  }
-  if (msg->conn->pcb.ip == NULL) {
-    msg->err = ERR_MEM;
-  }
-}
-
-/**
- * Create a new pcb of a specific type inside a netconn.
- * Called from netconn_new_with_proto_and_callback.
- *
- * @param m the api_msg describing the connection type
- */
-void
-lwip_netconn_do_newconn(void *m)
-{
-  struct api_msg *msg = (struct api_msg *)m;
-
-  msg->err = ERR_OK;
-  if (msg->conn->pcb.tcp == NULL) {
-    pcb_new(msg);
-  }
-  /* Else? This "new" connection already has a PCB allocated. */
-  /* Is this an error condition? Should it be deleted? */
-  /* We currently just are happy and return. */
-
-  TCPIP_APIMSG_ACK(msg);
-}
-
-/**
- * Create a new netconn (of a specific type) that has a callback function.
- * The corresponding pcb is NOT created!
- *
- * @param t the type of 'connection' to create (@see enum netconn_type)
- * @param callback a function to call on status changes (RX available, TX'ed)
- * @return a newly allocated struct netconn or
- *         NULL on memory error
- */
-struct netconn *
-netconn_alloc(enum netconn_type t, netconn_callback callback)
-{
-  struct netconn *conn;
-  int size;
-  u8_t init_flags = 0;
-
-  conn = (struct netconn *)memp_malloc(MEMP_NETCONN);
-  if (conn == NULL) {
-    return NULL;
-  }
-
-  conn->pending_err = ERR_OK;
-  conn->type = t;
-  conn->pcb.tcp = NULL;
-
-  /* If all sizes are the same, every compiler should optimize this switch to nothing */
-  switch (NETCONNTYPE_GROUP(t)) {
-#if LWIP_RAW
-    case NETCONN_RAW:
-      size = DEFAULT_RAW_RECVMBOX_SIZE;
-      break;
-#endif /* LWIP_RAW */
-#if LWIP_UDP
-    case NETCONN_UDP:
-      size = DEFAULT_UDP_RECVMBOX_SIZE;
-#if LWIP_NETBUF_RECVINFO
-      init_flags |= NETCONN_FLAG_PKTINFO;
-#endif /* LWIP_NETBUF_RECVINFO */
-      break;
-#endif /* LWIP_UDP */
-#if LWIP_TCP
-    case NETCONN_TCP:
-      size = DEFAULT_TCP_RECVMBOX_SIZE;
-      break;
-#endif /* LWIP_TCP */
-    default:
-      LWIP_ASSERT("netconn_alloc: undefined netconn_type", 0);
-      goto free_and_return;
-  }
-
-  if (sys_mbox_new(&conn->recvmbox, size) != ERR_OK) {
-    goto free_and_return;
-  }
-#if !LWIP_NETCONN_SEM_PER_THREAD
-  if (sys_sem_new(&conn->op_completed, 0) != ERR_OK) {
-    sys_mbox_free(&conn->recvmbox);
-    goto free_and_return;
-  }
-#endif
-
-#if LWIP_TCP
-  sys_mbox_set_invalid(&conn->acceptmbox);
-#endif
-  conn->state        = NETCONN_NONE;
-#if LWIP_SOCKET
-  /* initialize socket to -1 since 0 is a valid socket */
-  conn->socket       = -1;
-#endif /* LWIP_SOCKET */
-  conn->callback     = callback;
-#if LWIP_TCP
-  conn->current_msg  = NULL;
-#endif /* LWIP_TCP */
-#if LWIP_SO_SNDTIMEO
-  conn->send_timeout = 0;
-#endif /* LWIP_SO_SNDTIMEO */
-#if LWIP_SO_RCVTIMEO
-  conn->recv_timeout = 0;
-#endif /* LWIP_SO_RCVTIMEO */
-#if LWIP_SO_RCVBUF
-  conn->recv_bufsize = RECV_BUFSIZE_DEFAULT;
-  conn->recv_avail   = 0;
-#endif /* LWIP_SO_RCVBUF */
-#if LWIP_SO_LINGER
-  conn->linger = -1;
-#endif /* LWIP_SO_LINGER */
-  conn->flags = init_flags;
-  return conn;
-free_and_return:
-  memp_free(MEMP_NETCONN, conn);
-  return NULL;
-}
-
-/**
- * Delete a netconn and all its resources.
- * The pcb is NOT freed (since we might not be in the right thread context do this).
- *
- * @param conn the netconn to free
- */
-void
-netconn_free(struct netconn *conn)
-{
-  LWIP_ASSERT("PCB must be deallocated outside this function", conn->pcb.tcp == NULL);
-
-#if LWIP_NETCONN_FULLDUPLEX
-  /* in fullduplex, netconn is drained here */
-  netconn_drain(conn);
-#endif /* LWIP_NETCONN_FULLDUPLEX */
-
-  LWIP_ASSERT("recvmbox must be deallocated before calling this function",
-              !sys_mbox_valid(&conn->recvmbox));
-#if LWIP_TCP
-  LWIP_ASSERT("acceptmbox must be deallocated before calling this function",
-              !sys_mbox_valid(&conn->acceptmbox));
-#endif /* LWIP_TCP */
-
-#if !LWIP_NETCONN_SEM_PER_THREAD
-  sys_sem_free(&conn->op_completed);
-  sys_sem_set_invalid(&conn->op_completed);
-#endif
-
-  memp_free(MEMP_NETCONN, conn);
-}
-
-/**
- * Delete rcvmbox and acceptmbox of a netconn and free the left-over data in
- * these mboxes
- *
- * @param conn the netconn to free
- * @bytes_drained bytes drained from recvmbox
- * @accepts_drained pending connections drained from acceptmbox
- */
-static void
-netconn_drain(struct netconn *conn)
-{
-  void *mem;
-
-  /* This runs when mbox and netconn are marked as closed,
-     so we don't need to lock against rx packets */
-#if LWIP_NETCONN_FULLDUPLEX
-  LWIP_ASSERT("netconn marked closed", conn->flags & NETCONN_FLAG_MBOXINVALID);
-#endif /* LWIP_NETCONN_FULLDUPLEX */
-
-  /* Delete and drain the recvmbox. */
-  if (sys_mbox_valid(&conn->recvmbox)) {
-    while (sys_mbox_tryfetch(&conn->recvmbox, &mem) != SYS_MBOX_EMPTY) {
-#if LWIP_NETCONN_FULLDUPLEX
-      if (!lwip_netconn_is_deallocated_msg(mem))
-#endif /* LWIP_NETCONN_FULLDUPLEX */
-      {
-#if LWIP_TCP
-        if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) {
-          err_t err;
-          if (!lwip_netconn_is_err_msg(mem, &err)) {
-            pbuf_free((struct pbuf *)mem);
-          }
-        } else
-#endif /* LWIP_TCP */
-        {
-          netbuf_delete((struct netbuf *)mem);
-        }
-      }
-    }
-    sys_mbox_free(&conn->recvmbox);
-    sys_mbox_set_invalid(&conn->recvmbox);
-  }
-
-  /* Delete and drain the acceptmbox. */
-#if LWIP_TCP
-  if (sys_mbox_valid(&conn->acceptmbox)) {
-    while (sys_mbox_tryfetch(&conn->acceptmbox, &mem) != SYS_MBOX_EMPTY) {
-#if LWIP_NETCONN_FULLDUPLEX
-      if (!lwip_netconn_is_deallocated_msg(mem))
-#endif /* LWIP_NETCONN_FULLDUPLEX */
-      {
-        err_t err;
-        if (!lwip_netconn_is_err_msg(mem, &err)) {
-          struct netconn *newconn = (struct netconn *)mem;
-          /* Only tcp pcbs have an acceptmbox, so no need to check conn->type */
-          /* pcb might be set to NULL already by err_tcp() */
-          /* drain recvmbox */
-          netconn_drain(newconn);
-          if (newconn->pcb.tcp != NULL) {
-            tcp_abort(newconn->pcb.tcp);
-            newconn->pcb.tcp = NULL;
-          }
-          netconn_free(newconn);
-        }
-      }
-    }
-    sys_mbox_free(&conn->acceptmbox);
-    sys_mbox_set_invalid(&conn->acceptmbox);
-  }
-#endif /* LWIP_TCP */
-}
-
-#if LWIP_NETCONN_FULLDUPLEX
-static void
-netconn_mark_mbox_invalid(struct netconn *conn)
-{
-  int i, num_waiting;
-  void *msg = LWIP_CONST_CAST(void *, &netconn_deleted);
-
-  /* Prevent new calls/threads from reading from the mbox */
-  conn->flags |= NETCONN_FLAG_MBOXINVALID;
-
-  SYS_ARCH_LOCKED(num_waiting = conn->mbox_threads_waiting);
-  for (i = 0; i < num_waiting; i++) {
-    if (sys_mbox_valid_val(conn->recvmbox)) {
-      sys_mbox_trypost(&conn->recvmbox, msg);
-    } else {
-      sys_mbox_trypost(&conn->acceptmbox, msg);
-    }
-  }
-}
-#endif /* LWIP_NETCONN_FULLDUPLEX */
-
-#if LWIP_TCP
-/**
- * Internal helper function to close a TCP netconn: since this sometimes
- * doesn't work at the first attempt, this function is called from multiple
- * places.
- *
- * @param conn the TCP netconn to close
- */
-static err_t
-lwip_netconn_do_close_internal(struct netconn *conn  WRITE_DELAYED_PARAM)
-{
-  err_t err;
-  u8_t shut, shut_rx, shut_tx, shut_close;
-  u8_t close_finished = 0;
-  struct tcp_pcb *tpcb;
-#if LWIP_SO_LINGER
-  u8_t linger_wait_required = 0;
-#endif /* LWIP_SO_LINGER */
-
-  LWIP_ASSERT("invalid conn", (conn != NULL));
-  LWIP_ASSERT("this is for tcp netconns only", (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP));
-  LWIP_ASSERT("conn must be in state NETCONN_CLOSE", (conn->state == NETCONN_CLOSE));
-  LWIP_ASSERT("pcb already closed", (conn->pcb.tcp != NULL));
-  LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
-
-  tpcb = conn->pcb.tcp;
-  shut = conn->current_msg->msg.sd.shut;
-  shut_rx = shut & NETCONN_SHUT_RD;
-  shut_tx = shut & NETCONN_SHUT_WR;
-  /* shutting down both ends is the same as closing
-     (also if RD or WR side was shut down before already) */
-  if (shut == NETCONN_SHUT_RDWR) {
-    shut_close = 1;
-  } else if (shut_rx &&
-             ((tpcb->state == FIN_WAIT_1) ||
-              (tpcb->state == FIN_WAIT_2) ||
-              (tpcb->state == CLOSING))) {
-    shut_close = 1;
-  } else if (shut_tx && ((tpcb->flags & TF_RXCLOSED) != 0)) {
-    shut_close = 1;
-  } else {
-    shut_close = 0;
-  }
-
-  /* Set back some callback pointers */
-  if (shut_close) {
-    tcp_arg(tpcb, NULL);
-  }
-  if (tpcb->state == LISTEN) {
-    tcp_accept(tpcb, NULL);
-  } else {
-    /* some callbacks have to be reset if tcp_close is not successful */
-    if (shut_rx) {
-      tcp_recv(tpcb, NULL);
-      tcp_accept(tpcb, NULL);
-    }
-    if (shut_tx) {
-      tcp_sent(tpcb, NULL);
-    }
-    if (shut_close) {
-      tcp_poll(tpcb, NULL, 0);
-      tcp_err(tpcb, NULL);
-    }
-  }
-  /* Try to close the connection */
-  if (shut_close) {
-#if LWIP_SO_LINGER
-    /* check linger possibilites before calling tcp_close */
-    err = ERR_OK;
-    /* linger enabled/required at all? (i.e. is there untransmitted data left?) */
-    if ((conn->linger >= 0) && (conn->pcb.tcp->unsent || conn->pcb.tcp->unacked)) {
-      if ((conn->linger == 0)) {
-        /* data left but linger prevents waiting */
-        tcp_abort(tpcb);
-        tpcb = NULL;
-      } else if (conn->linger > 0) {
-        /* data left and linger says we should wait */
-        if (netconn_is_nonblocking(conn)) {
-          /* data left on a nonblocking netconn -> cannot linger */
-          err = ERR_WOULDBLOCK;
-        } else if ((s32_t)(sys_now() - conn->current_msg->msg.sd.time_started) >=
-                   (conn->linger * 1000)) {
-          /* data left but linger timeout has expired (this happens on further
-             calls to this function through poll_tcp */
-          tcp_abort(tpcb);
-          tpcb = NULL;
-        } else {
-          /* data left -> need to wait for ACK after successful close */
-          linger_wait_required = 1;
-        }
-      }
-    }
-    if ((err == ERR_OK) && (tpcb != NULL))
-#endif /* LWIP_SO_LINGER */
-    {
-      err = tcp_close(tpcb);
-    }
-  } else {
-    err = tcp_shutdown(tpcb, shut_rx, shut_tx);
-  }
-  if (err == ERR_OK) {
-    close_finished = 1;
-#if LWIP_SO_LINGER
-    if (linger_wait_required) {
-      /* wait for ACK of all unsent/unacked data by just getting called again */
-      close_finished = 0;
-      err = ERR_INPROGRESS;
-    }
-#endif /* LWIP_SO_LINGER */
-  } else {
-    if (err == ERR_MEM) {
-      /* Closing failed because of memory shortage, try again later. Even for
-         nonblocking netconns, we have to wait since no standard socket application
-         is prepared for close failing because of resource shortage.
-         Check the timeout: this is kind of an lwip addition to the standard sockets:
-         we wait for some time when failing to allocate a segment for the FIN */
-#if LWIP_SO_SNDTIMEO || LWIP_SO_LINGER
-      s32_t close_timeout = LWIP_TCP_CLOSE_TIMEOUT_MS_DEFAULT;
-#if LWIP_SO_SNDTIMEO
-      if (conn->send_timeout > 0) {
-        close_timeout = conn->send_timeout;
-      }
-#endif /* LWIP_SO_SNDTIMEO */
-#if LWIP_SO_LINGER
-      if (conn->linger >= 0) {
-        /* use linger timeout (seconds) */
-        close_timeout = conn->linger * 1000U;
-      }
-#endif
-      if ((s32_t)(sys_now() - conn->current_msg->msg.sd.time_started) >= close_timeout) {
-#else /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
-      if (conn->current_msg->msg.sd.polls_left == 0) {
-#endif /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
-        close_finished = 1;
-        if (shut_close) {
-          /* in this case, we want to RST the connection */
-          tcp_abort(tpcb);
-          err = ERR_OK;
-        }
-      }
-    } else {
-      /* Closing failed for a non-memory error: give up */
-      close_finished = 1;
-    }
-  }
-  if (close_finished) {
-    /* Closing done (succeeded, non-memory error, nonblocking error or timeout) */
-    sys_sem_t *op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
-    conn->current_msg->err = err;
-    conn->current_msg = NULL;
-    conn->state = NETCONN_NONE;
-    if (err == ERR_OK) {
-      if (shut_close) {
-        /* Set back some callback pointers as conn is going away */
-        conn->pcb.tcp = NULL;
-        /* Trigger select() in socket layer. Make sure everybody notices activity
-         on the connection, error first! */
-        API_EVENT(conn, NETCONN_EVT_ERROR, 0);
-      }
-      if (shut_rx) {
-        API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
-      }
-      if (shut_tx) {
-        API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
-      }
-    }
-#if LWIP_TCPIP_CORE_LOCKING
-    if (delayed)
-#endif
-    {
-      /* wake up the application task */
-      sys_sem_signal(op_completed_sem);
-    }
-    return ERR_OK;
-  }
-  if (!close_finished) {
-    /* Closing failed and we want to wait: restore some of the callbacks */
-    /* Closing of listen pcb will never fail! */
-    LWIP_ASSERT("Closing a listen pcb may not fail!", (tpcb->state != LISTEN));
-    if (shut_tx) {
-      tcp_sent(tpcb, sent_tcp);
-    }
-    /* when waiting for close, set up poll interval to 500ms */
-    tcp_poll(tpcb, poll_tcp, 1);
-    tcp_err(tpcb, err_tcp);
-    tcp_arg(tpcb, conn);
-    /* don't restore recv callback: we don't want to receive any more data */
-  }
-  /* If closing didn't succeed, we get called again either
-     from poll_tcp or from sent_tcp */
-  LWIP_ASSERT("err != ERR_OK", err != ERR_OK);
-  return err;
-}
-#endif /* LWIP_TCP */
-
-/**
- * Delete the pcb inside a netconn.
- * Called from netconn_delete.
- *
- * @param m the api_msg pointing to the connection
- */
-void
-lwip_netconn_do_delconn(void *m)
-{
-  struct api_msg *msg = (struct api_msg *)m;
-
-  enum netconn_state state = msg->conn->state;
-  LWIP_ASSERT("netconn state error", /* this only happens for TCP netconns */
-              (state == NETCONN_NONE) || (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP));
-#if LWIP_NETCONN_FULLDUPLEX
-  /* In full duplex mode, blocking write/connect is aborted with ERR_CLSD */
-  if (state != NETCONN_NONE) {
-    if ((state == NETCONN_WRITE) ||
-        ((state == NETCONN_CONNECT) && !IN_NONBLOCKING_CONNECT(msg->conn))) {
-      /* close requested, abort running write/connect */
-      sys_sem_t *op_completed_sem;
-      LWIP_ASSERT("msg->conn->current_msg != NULL", msg->conn->current_msg != NULL);
-      op_completed_sem = LWIP_API_MSG_SEM(msg->conn->current_msg);
-      msg->conn->current_msg->err = ERR_CLSD;
-      msg->conn->current_msg = NULL;
-      msg->conn->state = NETCONN_NONE;
-      sys_sem_signal(op_completed_sem);
-    }
-  }
-#else /* LWIP_NETCONN_FULLDUPLEX */
-  if (((state != NETCONN_NONE) &&
-       (state != NETCONN_LISTEN) &&
-       (state != NETCONN_CONNECT)) ||
-      ((state == NETCONN_CONNECT) && !IN_NONBLOCKING_CONNECT(msg->conn))) {
-    /* This means either a blocking write or blocking connect is running
-       (nonblocking write returns and sets state to NONE) */
-    msg->err = ERR_INPROGRESS;
-  } else
-#endif /* LWIP_NETCONN_FULLDUPLEX */
-  {
-    LWIP_ASSERT("blocking connect in progress",
-                (state != NETCONN_CONNECT) || IN_NONBLOCKING_CONNECT(msg->conn));
-    msg->err = ERR_OK;
-#if LWIP_NETCONN_FULLDUPLEX
-    /* Mark mboxes invalid */
-    netconn_mark_mbox_invalid(msg->conn);
-#else /* LWIP_NETCONN_FULLDUPLEX */
-    netconn_drain(msg->conn);
-#endif /* LWIP_NETCONN_FULLDUPLEX */
-
-    if (msg->conn->pcb.tcp != NULL) {
-
-      switch (NETCONNTYPE_GROUP(msg->conn->type)) {
-#if LWIP_RAW
-        case NETCONN_RAW:
-          raw_remove(msg->conn->pcb.raw);
-          break;
-#endif /* LWIP_RAW */
-#if LWIP_UDP
-        case NETCONN_UDP:
-          msg->conn->pcb.udp->recv_arg = NULL;
-          udp_remove(msg->conn->pcb.udp);
-          break;
-#endif /* LWIP_UDP */
-#if LWIP_TCP
-        case NETCONN_TCP:
-          LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL);
-          msg->conn->state = NETCONN_CLOSE;
-          msg->msg.sd.shut = NETCONN_SHUT_RDWR;
-          msg->conn->current_msg = msg;
-#if LWIP_TCPIP_CORE_LOCKING
-          if (lwip_netconn_do_close_internal(msg->conn, 0) != ERR_OK) {
-            LWIP_ASSERT("state!", msg->conn->state == NETCONN_CLOSE);
-            UNLOCK_TCPIP_CORE();
-            sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0);
-            LOCK_TCPIP_CORE();
-            LWIP_ASSERT("state!", msg->conn->state == NETCONN_NONE);
-          }
-#else /* LWIP_TCPIP_CORE_LOCKING */
-          lwip_netconn_do_close_internal(msg->conn);
-#endif /* LWIP_TCPIP_CORE_LOCKING */
-          /* API_EVENT is called inside lwip_netconn_do_close_internal, before releasing
-             the application thread, so we can return at this point! */
-          return;
-#endif /* LWIP_TCP */
-        default:
-          break;
-      }
-      msg->conn->pcb.tcp = NULL;
-    }
-    /* tcp netconns don't come here! */
-
-    /* @todo: this lets select make the socket readable and writable,
-       which is wrong! errfd instead? */
-    API_EVENT(msg->conn, NETCONN_EVT_RCVPLUS, 0);
-    API_EVENT(msg->conn, NETCONN_EVT_SENDPLUS, 0);
-  }
-  if (sys_sem_valid(LWIP_API_MSG_SEM(msg))) {
-    TCPIP_APIMSG_ACK(msg);
-  }
-}
-
-/**
- * Bind a pcb contained in a netconn
- * Called from netconn_bind.
- *
- * @param m the api_msg pointing to the connection and containing
- *          the IP address and port to bind to
- */
-void
-lwip_netconn_do_bind(void *m)
-{
-  struct api_msg *msg = (struct api_msg *)m;
-  err_t err;
-
-  if (msg->conn->pcb.tcp != NULL) {
-    switch (NETCONNTYPE_GROUP(msg->conn->type)) {
-#if LWIP_RAW
-      case NETCONN_RAW:
-        err = raw_bind(msg->conn->pcb.raw, API_EXPR_REF(msg->msg.bc.ipaddr));
-        break;
-#endif /* LWIP_RAW */
-#if LWIP_UDP
-      case NETCONN_UDP:
-        err = udp_bind(msg->conn->pcb.udp, API_EXPR_REF(msg->msg.bc.ipaddr), msg->msg.bc.port);
-        break;
-#endif /* LWIP_UDP */
-#if LWIP_TCP
-      case NETCONN_TCP:
-        err = tcp_bind(msg->conn->pcb.tcp, API_EXPR_REF(msg->msg.bc.ipaddr), msg->msg.bc.port);
-        break;
-#endif /* LWIP_TCP */
-      default:
-        err = ERR_VAL;
-        break;
-    }
-  } else {
-    err = ERR_VAL;
-  }
-  msg->err = err;
-  TCPIP_APIMSG_ACK(msg);
-}
-/**
- * Bind a pcb contained in a netconn to an interface
- * Called from netconn_bind_if.
- *
- * @param m the api_msg pointing to the connection and containing
- *          the IP address and port to bind to
- */
-void
-lwip_netconn_do_bind_if(void *m)
-{
-  struct netif *netif;
-  struct api_msg *msg = (struct api_msg *)m;
-  err_t err;
-
-  netif = netif_get_by_index(msg->msg.bc.if_idx);
-
-  if ((netif != NULL) && (msg->conn->pcb.tcp != NULL)) {
-    err = ERR_OK;
-    switch (NETCONNTYPE_GROUP(msg->conn->type)) {
-#if LWIP_RAW
-      case NETCONN_RAW:
-        raw_bind_netif(msg->conn->pcb.raw, netif);
-        break;
-#endif /* LWIP_RAW */
-#if LWIP_UDP
-      case NETCONN_UDP:
-        udp_bind_netif(msg->conn->pcb.udp, netif);
-        break;
-#endif /* LWIP_UDP */
-#if LWIP_TCP
-      case NETCONN_TCP:
-        tcp_bind_netif(msg->conn->pcb.tcp, netif);
-        break;
-#endif /* LWIP_TCP */
-      default:
-        err = ERR_VAL;
-        break;
-    }
-  } else {
-    err = ERR_VAL;
-  }
-  msg->err = err;
-  TCPIP_APIMSG_ACK(msg);
-}
-
-#if LWIP_TCP
-/**
- * TCP callback function if a connection (opened by tcp_connect/lwip_netconn_do_connect) has
- * been established (or reset by the remote host).
- *
- * @see tcp.h (struct tcp_pcb.connected) for parameters and return values
- */
-static err_t
-lwip_netconn_do_connected(void *arg, struct tcp_pcb *pcb, err_t err)
-{
-  struct netconn *conn;
-  int was_blocking;
-  sys_sem_t *op_completed_sem = NULL;
-
-  LWIP_UNUSED_ARG(pcb);
-
-  conn = (struct netconn *)arg;
-
-  if (conn == NULL) {
-    return ERR_VAL;
-  }
-
-  LWIP_ASSERT("conn->state == NETCONN_CONNECT", conn->state == NETCONN_CONNECT);
-  LWIP_ASSERT("(conn->current_msg != NULL) || conn->in_non_blocking_connect",
-              (conn->current_msg != NULL) || IN_NONBLOCKING_CONNECT(conn));
-
-  if (conn->current_msg != NULL) {
-    conn->current_msg->err = err;
-    op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
-  }
-  if ((NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) && (err == ERR_OK)) {
-    setup_tcp(conn);
-  }
-  was_blocking = !IN_NONBLOCKING_CONNECT(conn);
-  SET_NONBLOCKING_CONNECT(conn, 0);
-  LWIP_ASSERT("blocking connect state error",
-              (was_blocking && op_completed_sem != NULL) ||
-              (!was_blocking && op_completed_sem == NULL));
-  conn->current_msg = NULL;
-  conn->state = NETCONN_NONE;
-  API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
-
-  if (was_blocking) {
-    sys_sem_signal(op_completed_sem);
-  }
-  return ERR_OK;
-}
-#endif /* LWIP_TCP */
-
-/**
- * Connect a pcb contained inside a netconn
- * Called from netconn_connect.
- *
- * @param m the api_msg pointing to the connection and containing
- *          the IP address and port to connect to
- */
-void
-lwip_netconn_do_connect(void *m)
-{
-  struct api_msg *msg = (struct api_msg *)m;
-  err_t err;
-
-  if (msg->conn->pcb.tcp == NULL) {
-    /* This may happen when calling netconn_connect() a second time */
-    err = ERR_CLSD;
-  } else {
-    switch (NETCONNTYPE_GROUP(msg->conn->type)) {
-#if LWIP_RAW
-      case NETCONN_RAW:
-        err = raw_connect(msg->conn->pcb.raw, API_EXPR_REF(msg->msg.bc.ipaddr));
-        break;
-#endif /* LWIP_RAW */
-#if LWIP_UDP
-      case NETCONN_UDP:
-        err = udp_connect(msg->conn->pcb.udp, API_EXPR_REF(msg->msg.bc.ipaddr), msg->msg.bc.port);
-        break;
-#endif /* LWIP_UDP */
-#if LWIP_TCP
-      case NETCONN_TCP:
-        /* Prevent connect while doing any other action. */
-        if (msg->conn->state == NETCONN_CONNECT) {
-          err = ERR_ALREADY;
-        } else if (msg->conn->state != NETCONN_NONE) {
-          err = ERR_ISCONN;
-        } else {
-          setup_tcp(msg->conn);
-          err = tcp_connect(msg->conn->pcb.tcp, API_EXPR_REF(msg->msg.bc.ipaddr),
-                            msg->msg.bc.port, lwip_netconn_do_connected);
-          if (err == ERR_OK) {
-            u8_t non_blocking = netconn_is_nonblocking(msg->conn);
-            msg->conn->state = NETCONN_CONNECT;
-            SET_NONBLOCKING_CONNECT(msg->conn, non_blocking);
-            if (non_blocking) {
-              err = ERR_INPROGRESS;
-            } else {
-              msg->conn->current_msg = msg;
-              /* sys_sem_signal() is called from lwip_netconn_do_connected (or err_tcp()),
-                 when the connection is established! */
-#if LWIP_TCPIP_CORE_LOCKING
-              LWIP_ASSERT("state!", msg->conn->state == NETCONN_CONNECT);
-              UNLOCK_TCPIP_CORE();
-              sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0);
-              LOCK_TCPIP_CORE();
-              LWIP_ASSERT("state!", msg->conn->state != NETCONN_CONNECT);
-#endif /* LWIP_TCPIP_CORE_LOCKING */
-              return;
-            }
-          }
-        }
-        break;
-#endif /* LWIP_TCP */
-      default:
-        LWIP_ERROR("Invalid netconn type", 0, do {
-          err = ERR_VAL;
-        } while (0));
-        break;
-    }
-  }
-  msg->err = err;
-  /* For all other protocols, netconn_connect() calls netconn_apimsg(),
-     so use TCPIP_APIMSG_ACK() here. */
-  TCPIP_APIMSG_ACK(msg);
-}
-
-/**
- * Disconnect a pcb contained inside a netconn
- * Only used for UDP netconns.
- * Called from netconn_disconnect.
- *
- * @param m the api_msg pointing to the connection to disconnect
- */
-void
-lwip_netconn_do_disconnect(void *m)
-{
-  struct api_msg *msg = (struct api_msg *)m;
-
-#if LWIP_UDP
-  if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) {
-    udp_disconnect(msg->conn->pcb.udp);
-    msg->err = ERR_OK;
-  } else
-#endif /* LWIP_UDP */
-  {
-    msg->err = ERR_VAL;
-  }
-  TCPIP_APIMSG_ACK(msg);
-}
-
-#if LWIP_TCP
-/**
- * Set a TCP pcb contained in a netconn into listen mode
- * Called from netconn_listen.
- *
- * @param m the api_msg pointing to the connection
- */
-void
-lwip_netconn_do_listen(void *m)
-{
-  struct api_msg *msg = (struct api_msg *)m;
-  err_t err;
-
-  if (msg->conn->pcb.tcp != NULL) {
-    if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) {
-      if (msg->conn->state == NETCONN_NONE) {
-        struct tcp_pcb *lpcb;
-        if (msg->conn->pcb.tcp->state != CLOSED) {
-          /* connection is not closed, cannot listen */
-          err = ERR_VAL;
-        } else {
-          u8_t backlog;
-#if TCP_LISTEN_BACKLOG
-          backlog = msg->msg.lb.backlog;
-#else  /* TCP_LISTEN_BACKLOG */
-          backlog = TCP_DEFAULT_LISTEN_BACKLOG;
-#endif /* TCP_LISTEN_BACKLOG */
-#if LWIP_IPV4 && LWIP_IPV6
-          /* "Socket API like" dual-stack support: If IP to listen to is IP6_ADDR_ANY,
-            * and NETCONN_FLAG_IPV6_V6ONLY is NOT set, use IP_ANY_TYPE to listen
-            */
-          if (ip_addr_cmp(&msg->conn->pcb.ip->local_ip, IP6_ADDR_ANY) &&
-              (netconn_get_ipv6only(msg->conn) == 0)) {
-            /* change PCB type to IPADDR_TYPE_ANY */
-            IP_SET_TYPE_VAL(msg->conn->pcb.tcp->local_ip,  IPADDR_TYPE_ANY);
-            IP_SET_TYPE_VAL(msg->conn->pcb.tcp->remote_ip, IPADDR_TYPE_ANY);
-          }
-#endif /* LWIP_IPV4 && LWIP_IPV6 */
-
-          lpcb = tcp_listen_with_backlog_and_err(msg->conn->pcb.tcp, backlog, &err);
-
-          if (lpcb == NULL) {
-            /* in this case, the old pcb is still allocated */
-          } else {
-            /* delete the recvmbox and allocate the acceptmbox */
-            if (sys_mbox_valid(&msg->conn->recvmbox)) {
-              /** @todo: should we drain the recvmbox here? */
-              sys_mbox_free(&msg->conn->recvmbox);
-              sys_mbox_set_invalid(&msg->conn->recvmbox);
-            }
-            err = ERR_OK;
-            if (!sys_mbox_valid(&msg->conn->acceptmbox)) {
-              err = sys_mbox_new(&msg->conn->acceptmbox, DEFAULT_ACCEPTMBOX_SIZE);
-            }
-            if (err == ERR_OK) {
-              msg->conn->state = NETCONN_LISTEN;
-              msg->conn->pcb.tcp = lpcb;
-              tcp_arg(msg->conn->pcb.tcp, msg->conn);
-              tcp_accept(msg->conn->pcb.tcp, accept_function);
-            } else {
-              /* since the old pcb is already deallocated, free lpcb now */
-              tcp_close(lpcb);
-              msg->conn->pcb.tcp = NULL;
-            }
-          }
-        }
-      } else if (msg->conn->state == NETCONN_LISTEN) {
-        /* already listening, allow updating of the backlog */
-        err = ERR_OK;
-        tcp_backlog_set(msg->conn->pcb.tcp, msg->msg.lb.backlog);
-      } else {
-        err = ERR_CONN;
-      }
-    } else {
-      err = ERR_ARG;
-    }
-  } else {
-    err = ERR_CONN;
-  }
-  msg->err = err;
-  TCPIP_APIMSG_ACK(msg);
-}
-#endif /* LWIP_TCP */
-
-/**
- * Send some data on a RAW or UDP pcb contained in a netconn
- * Called from netconn_send
- *
- * @param m the api_msg pointing to the connection
- */
-void
-lwip_netconn_do_send(void *m)
-{
-  struct api_msg *msg = (struct api_msg *)m;
-
-  err_t err = netconn_err(msg->conn);
-  if (err == ERR_OK) {
-    if (msg->conn->pcb.tcp != NULL) {
-      switch (NETCONNTYPE_GROUP(msg->conn->type)) {
-#if LWIP_RAW
-        case NETCONN_RAW:
-          if (ip_addr_isany(&msg->msg.b->addr) || IP_IS_ANY_TYPE_VAL(msg->msg.b->addr)) {
-            err = raw_send(msg->conn->pcb.raw, msg->msg.b->p);
-          } else {
-            err = raw_sendto(msg->conn->pcb.raw, msg->msg.b->p, &msg->msg.b->addr);
-          }
-          break;
-#endif
-#if LWIP_UDP
-        case NETCONN_UDP:
-#if LWIP_CHECKSUM_ON_COPY
-          if (ip_addr_isany(&msg->msg.b->addr) || IP_IS_ANY_TYPE_VAL(msg->msg.b->addr)) {
-            err = udp_send_chksum(msg->conn->pcb.udp, msg->msg.b->p,
-                                  msg->msg.b->flags & NETBUF_FLAG_CHKSUM, msg->msg.b->toport_chksum);
-          } else {
-            err = udp_sendto_chksum(msg->conn->pcb.udp, msg->msg.b->p,
-                                    &msg->msg.b->addr, msg->msg.b->port,
-                                    msg->msg.b->flags & NETBUF_FLAG_CHKSUM, msg->msg.b->toport_chksum);
-          }
-#else /* LWIP_CHECKSUM_ON_COPY */
-          if (ip_addr_isany_val(msg->msg.b->addr) || IP_IS_ANY_TYPE_VAL(msg->msg.b->addr)) {
-            err = udp_send(msg->conn->pcb.udp, msg->msg.b->p);
-          } else {
-            err = udp_sendto(msg->conn->pcb.udp, msg->msg.b->p, &msg->msg.b->addr, msg->msg.b->port);
-          }
-#endif /* LWIP_CHECKSUM_ON_COPY */
-          break;
-#endif /* LWIP_UDP */
-        default:
-          err = ERR_CONN;
-          break;
-      }
-    } else {
-      err = ERR_CONN;
-    }
-  }
-  msg->err = err;
-  TCPIP_APIMSG_ACK(msg);
-}
-
-#if LWIP_TCP
-/**
- * Indicate data has been received from a TCP pcb contained in a netconn
- * Called from netconn_recv
- *
- * @param m the api_msg pointing to the connection
- */
-void
-lwip_netconn_do_recv(void *m)
-{
-  struct api_msg *msg = (struct api_msg *)m;
-
-  msg->err = ERR_OK;
-  if (msg->conn->pcb.tcp != NULL) {
-    if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) {
-      size_t remaining = msg->msg.r.len;
-      do {
-        u16_t recved = (u16_t)((remaining > 0xffff) ? 0xffff : remaining);
-        tcp_recved(msg->conn->pcb.tcp, recved);
-        remaining -= recved;
-      } while (remaining != 0);
-    }
-  }
-  TCPIP_APIMSG_ACK(msg);
-}
-
-#if TCP_LISTEN_BACKLOG
-/** Indicate that a TCP pcb has been accepted
- * Called from netconn_accept
- *
- * @param m the api_msg pointing to the connection
- */
-void
-lwip_netconn_do_accepted(void *m)
-{
-  struct api_msg *msg = (struct api_msg *)m;
-
-  msg->err = ERR_OK;
-  if (msg->conn->pcb.tcp != NULL) {
-    if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) {
-      tcp_backlog_accepted(msg->conn->pcb.tcp);
-    }
-  }
-  TCPIP_APIMSG_ACK(msg);
-}
-#endif /* TCP_LISTEN_BACKLOG */
-
-/**
- * See if more data needs to be written from a previous call to netconn_write.
- * Called initially from lwip_netconn_do_write. If the first call can't send all data
- * (because of low memory or empty send-buffer), this function is called again
- * from sent_tcp() or poll_tcp() to send more data. If all data is sent, the
- * blocking application thread (waiting in netconn_write) is released.
- *
- * @param conn netconn (that is currently in state NETCONN_WRITE) to process
- * @return ERR_OK
- *         ERR_MEM if LWIP_TCPIP_CORE_LOCKING=1 and sending hasn't yet finished
- */
-static err_t
-lwip_netconn_do_writemore(struct netconn *conn  WRITE_DELAYED_PARAM)
-{
-  err_t err;
-  const void *dataptr;
-  u16_t len, available;
-  u8_t write_finished = 0;
-  size_t diff;
-  u8_t dontblock;
-  u8_t apiflags;
-  u8_t write_more;
-
-  LWIP_ASSERT("conn != NULL", conn != NULL);
-  LWIP_ASSERT("conn->state == NETCONN_WRITE", (conn->state == NETCONN_WRITE));
-  LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
-  LWIP_ASSERT("conn->pcb.tcp != NULL", conn->pcb.tcp != NULL);
-  LWIP_ASSERT("conn->current_msg->msg.w.offset < conn->current_msg->msg.w.len",
-              conn->current_msg->msg.w.offset < conn->current_msg->msg.w.len);
-  LWIP_ASSERT("conn->current_msg->msg.w.vector_cnt > 0", conn->current_msg->msg.w.vector_cnt > 0);
-
-  apiflags = conn->current_msg->msg.w.apiflags;
-  dontblock = netconn_is_nonblocking(conn) || (apiflags & NETCONN_DONTBLOCK);
-
-#if LWIP_SO_SNDTIMEO
-  if ((conn->send_timeout != 0) &&
-      ((s32_t)(sys_now() - conn->current_msg->msg.w.time_started) >= conn->send_timeout)) {
-    write_finished = 1;
-    if (conn->current_msg->msg.w.offset == 0) {
-      /* nothing has been written */
-      err = ERR_WOULDBLOCK;
-    } else {
-      /* partial write */
-      err = ERR_OK;
-    }
-  } else
-#endif /* LWIP_SO_SNDTIMEO */
-  {
-    do {
-      dataptr = (const u8_t *)conn->current_msg->msg.w.vector->ptr + conn->current_msg->msg.w.vector_off;
-      diff = conn->current_msg->msg.w.vector->len - conn->current_msg->msg.w.vector_off;
-      if (diff > 0xffffUL) { /* max_u16_t */
-        len = 0xffff;
-        apiflags |= TCP_WRITE_FLAG_MORE;
-      } else {
-        len = (u16_t)diff;
-      }
-      available = tcp_sndbuf(conn->pcb.tcp);
-      if (available < len) {
-        /* don't try to write more than sendbuf */
-        len = available;
-        if (dontblock) {
-          if (!len) {
-            /* set error according to partial write or not */
-            err = (conn->current_msg->msg.w.offset == 0) ? ERR_WOULDBLOCK : ERR_OK;
-            goto err_mem;
-          }
-        } else {
-          apiflags |= TCP_WRITE_FLAG_MORE;
-        }
-      }
-      LWIP_ASSERT("lwip_netconn_do_writemore: invalid length!",
-                  ((conn->current_msg->msg.w.vector_off + len) <= conn->current_msg->msg.w.vector->len));
-      /* we should loop around for more sending in the following cases:
-           1) We couldn't finish the current vector because of 16-bit size limitations.
-              tcp_write() and tcp_sndbuf() both are limited to 16-bit sizes
-           2) We are sending the remainder of the current vector and have more */
-      if ((len == 0xffff && diff > 0xffffUL) ||
-          (len == (u16_t)diff && conn->current_msg->msg.w.vector_cnt > 1)) {
-        write_more = 1;
-        apiflags |= TCP_WRITE_FLAG_MORE;
-      } else {
-        write_more = 0;
-      }
-      err = tcp_write(conn->pcb.tcp, dataptr, len, apiflags);
-      if (err == ERR_OK) {
-        conn->current_msg->msg.w.offset += len;
-        conn->current_msg->msg.w.vector_off += len;
-        /* check if current vector is finished */
-        if (conn->current_msg->msg.w.vector_off == conn->current_msg->msg.w.vector->len) {
-          conn->current_msg->msg.w.vector_cnt--;
-          /* if we have additional vectors, move on to them */
-          if (conn->current_msg->msg.w.vector_cnt > 0) {
-            conn->current_msg->msg.w.vector++;
-            conn->current_msg->msg.w.vector_off = 0;
-          }
-        }
-      }
-    } while (write_more && err == ERR_OK);
-    /* if OK or memory error, check available space */
-    if ((err == ERR_OK) || (err == ERR_MEM)) {
-err_mem:
-      if (dontblock && (conn->current_msg->msg.w.offset < conn->current_msg->msg.w.len)) {
-        /* non-blocking write did not write everything: mark the pcb non-writable
-           and let poll_tcp check writable space to mark the pcb writable again */
-        API_EVENT(conn, NETCONN_EVT_SENDMINUS, 0);
-        conn->flags |= NETCONN_FLAG_CHECK_WRITESPACE;
-      } else if ((tcp_sndbuf(conn->pcb.tcp) <= TCP_SNDLOWAT) ||
-                 (tcp_sndqueuelen(conn->pcb.tcp) >= TCP_SNDQUEUELOWAT)) {
-        /* The queued byte- or pbuf-count exceeds the configured low-water limit,
-           let select mark this pcb as non-writable. */
-        API_EVENT(conn, NETCONN_EVT_SENDMINUS, 0);
-      }
-    }
-
-    if (err == ERR_OK) {
-      err_t out_err;
-      if ((conn->current_msg->msg.w.offset == conn->current_msg->msg.w.len) || dontblock) {
-        /* return sent length (caller reads length from msg.w.offset) */
-        write_finished = 1;
-      }
-      out_err = tcp_output(conn->pcb.tcp);
-      if (out_err == ERR_RTE) {
-        /* If tcp_output fails because no route is found,
-           don't try writing any more but return the error
-           to the application thread. */
-        err = out_err;
-        write_finished = 1;
-      }
-    } else if (err == ERR_MEM) {
-      /* If ERR_MEM, we wait for sent_tcp or poll_tcp to be called.
-         For blocking sockets, we do NOT return to the application
-         thread, since ERR_MEM is only a temporary error! Non-blocking
-         will remain non-writable until sent_tcp/poll_tcp is called */
-
-      /* tcp_write returned ERR_MEM, try tcp_output anyway */
-      err_t out_err = tcp_output(conn->pcb.tcp);
-      if (out_err == ERR_RTE) {
-        /* If tcp_output fails because no route is found,
-           don't try writing any more but return the error
-           to the application thread. */
-        err = out_err;
-        write_finished = 1;
-      } else if (dontblock) {
-        /* non-blocking write is done on ERR_MEM, set error according
-           to partial write or not */
-        err = (conn->current_msg->msg.w.offset == 0) ? ERR_WOULDBLOCK : ERR_OK;
-        write_finished = 1;
-      }
-    } else {
-      /* On errors != ERR_MEM, we don't try writing any more but return
-         the error to the application thread. */
-      write_finished = 1;
-    }
-  }
-  if (write_finished) {
-    /* everything was written: set back connection state
-       and back to application task */
-    sys_sem_t *op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
-    conn->current_msg->err = err;
-    conn->current_msg = NULL;
-    conn->state = NETCONN_NONE;
-#if LWIP_TCPIP_CORE_LOCKING
-    if (delayed)
-#endif
-    {
-      sys_sem_signal(op_completed_sem);
-    }
-  }
-#if LWIP_TCPIP_CORE_LOCKING
-  else {
-    return ERR_MEM;
-  }
-#endif
-  return ERR_OK;
-}
-#endif /* LWIP_TCP */
-
-/**
- * Send some data on a TCP pcb contained in a netconn
- * Called from netconn_write
- *
- * @param m the api_msg pointing to the connection
- */
-void
-lwip_netconn_do_write(void *m)
-{
-  struct api_msg *msg = (struct api_msg *)m;
-
-  err_t err = netconn_err(msg->conn);
-  if (err == ERR_OK) {
-    if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) {
-#if LWIP_TCP
-      if (msg->conn->state != NETCONN_NONE) {
-        /* netconn is connecting, closing or in blocking write */
-        err = ERR_INPROGRESS;
-      } else if (msg->conn->pcb.tcp != NULL) {
-        msg->conn->state = NETCONN_WRITE;
-        /* set all the variables used by lwip_netconn_do_writemore */
-        LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL);
-        LWIP_ASSERT("msg->msg.w.len != 0", msg->msg.w.len != 0);
-        msg->conn->current_msg = msg;
-#if LWIP_TCPIP_CORE_LOCKING
-        if (lwip_netconn_do_writemore(msg->conn, 0) != ERR_OK) {
-          LWIP_ASSERT("state!", msg->conn->state == NETCONN_WRITE);
-          UNLOCK_TCPIP_CORE();
-          sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0);
-          LOCK_TCPIP_CORE();
-          LWIP_ASSERT("state!", msg->conn->state != NETCONN_WRITE);
-        }
-#else /* LWIP_TCPIP_CORE_LOCKING */
-        lwip_netconn_do_writemore(msg->conn);
-#endif /* LWIP_TCPIP_CORE_LOCKING */
-        /* for both cases: if lwip_netconn_do_writemore was called, don't ACK the APIMSG
-           since lwip_netconn_do_writemore ACKs it! */
-        return;
-      } else {
-        err = ERR_CONN;
-      }
-#else /* LWIP_TCP */
-      err = ERR_VAL;
-#endif /* LWIP_TCP */
-#if (LWIP_UDP || LWIP_RAW)
-    } else {
-      err = ERR_VAL;
-#endif /* (LWIP_UDP || LWIP_RAW) */
-    }
-  }
-  msg->err = err;
-  TCPIP_APIMSG_ACK(msg);
-}
-
-/**
- * Return a connection's local or remote address
- * Called from netconn_getaddr
- *
- * @param m the api_msg pointing to the connection
- */
-void
-lwip_netconn_do_getaddr(void *m)
-{
-  struct api_msg *msg = (struct api_msg *)m;
-
-  if (msg->conn->pcb.ip != NULL) {
-    if (msg->msg.ad.local) {
-      ip_addr_copy(API_EXPR_DEREF(msg->msg.ad.ipaddr),
-                   msg->conn->pcb.ip->local_ip);
-    } else {
-      ip_addr_copy(API_EXPR_DEREF(msg->msg.ad.ipaddr),
-                   msg->conn->pcb.ip->remote_ip);
-    }
-
-    msg->err = ERR_OK;
-    switch (NETCONNTYPE_GROUP(msg->conn->type)) {
-#if LWIP_RAW
-      case NETCONN_RAW:
-        if (msg->msg.ad.local) {
-          API_EXPR_DEREF(msg->msg.ad.port) = msg->conn->pcb.raw->protocol;
-        } else {
-          /* return an error as connecting is only a helper for upper layers */
-          msg->err = ERR_CONN;
-        }
-        break;
-#endif /* LWIP_RAW */
-#if LWIP_UDP
-      case NETCONN_UDP:
-        if (msg->msg.ad.local) {
-          API_EXPR_DEREF(msg->msg.ad.port) = msg->conn->pcb.udp->local_port;
-        } else {
-          if ((msg->conn->pcb.udp->flags & UDP_FLAGS_CONNECTED) == 0) {
-            msg->err = ERR_CONN;
-          } else {
-            API_EXPR_DEREF(msg->msg.ad.port) = msg->conn->pcb.udp->remote_port;
-          }
-        }
-        break;
-#endif /* LWIP_UDP */
-#if LWIP_TCP
-      case NETCONN_TCP:
-        if ((msg->msg.ad.local == 0) &&
-            ((msg->conn->pcb.tcp->state == CLOSED) || (msg->conn->pcb.tcp->state == LISTEN))) {
-          /* pcb is not connected and remote name is requested */
-          msg->err = ERR_CONN;
-        } else {
-          API_EXPR_DEREF(msg->msg.ad.port) = (msg->msg.ad.local ? msg->conn->pcb.tcp->local_port : msg->conn->pcb.tcp->remote_port);
-        }
-        break;
-#endif /* LWIP_TCP */
-      default:
-        LWIP_ASSERT("invalid netconn_type", 0);
-        break;
-    }
-  } else {
-    msg->err = ERR_CONN;
-  }
-  TCPIP_APIMSG_ACK(msg);
-}
-
-/**
- * Close or half-shutdown a TCP pcb contained in a netconn
- * Called from netconn_close
- * In contrast to closing sockets, the netconn is not deallocated.
- *
- * @param m the api_msg pointing to the connection
- */
-void
-lwip_netconn_do_close(void *m)
-{
-  struct api_msg *msg = (struct api_msg *)m;
-
-#if LWIP_TCP
-  enum netconn_state state = msg->conn->state;
-  /* First check if this is a TCP netconn and if it is in a correct state
-      (LISTEN doesn't support half shutdown) */
-  if ((msg->conn->pcb.tcp != NULL) &&
-      (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) &&
-      ((msg->msg.sd.shut == NETCONN_SHUT_RDWR) || (state != NETCONN_LISTEN))) {
-    /* Check if we are in a connected state */
-    if (state == NETCONN_CONNECT) {
-      /* TCP connect in progress: cannot shutdown */
-      msg->err = ERR_CONN;
-    } else if (state == NETCONN_WRITE) {
-#if LWIP_NETCONN_FULLDUPLEX
-      if (msg->msg.sd.shut & NETCONN_SHUT_WR) {
-        /* close requested, abort running write */
-        sys_sem_t *write_completed_sem;
-        LWIP_ASSERT("msg->conn->current_msg != NULL", msg->conn->current_msg != NULL);
-        write_completed_sem = LWIP_API_MSG_SEM(msg->conn->current_msg);
-        msg->conn->current_msg->err = ERR_CLSD;
-        msg->conn->current_msg = NULL;
-        msg->conn->state = NETCONN_NONE;
-        state = NETCONN_NONE;
-        sys_sem_signal(write_completed_sem);
-      } else {
-        LWIP_ASSERT("msg->msg.sd.shut == NETCONN_SHUT_RD", msg->msg.sd.shut == NETCONN_SHUT_RD);
-        /* In this case, let the write continue and do not interfere with
-           conn->current_msg or conn->state! */
-        msg->err = tcp_shutdown(msg->conn->pcb.tcp, 1, 0);
-      }
-    }
-    if (state == NETCONN_NONE) {
-#else /* LWIP_NETCONN_FULLDUPLEX */
-      msg->err = ERR_INPROGRESS;
-    } else {
-#endif /* LWIP_NETCONN_FULLDUPLEX */
-      if (msg->msg.sd.shut & NETCONN_SHUT_RD) {
-#if LWIP_NETCONN_FULLDUPLEX
-        /* Mark mboxes invalid */
-        netconn_mark_mbox_invalid(msg->conn);
-#else /* LWIP_NETCONN_FULLDUPLEX */
-        netconn_drain(msg->conn);
-#endif /* LWIP_NETCONN_FULLDUPLEX */
-      }
-      LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL);
-      msg->conn->state = NETCONN_CLOSE;
-      msg->conn->current_msg = msg;
-#if LWIP_TCPIP_CORE_LOCKING
-      if (lwip_netconn_do_close_internal(msg->conn, 0) != ERR_OK) {
-        LWIP_ASSERT("state!", msg->conn->state == NETCONN_CLOSE);
-        UNLOCK_TCPIP_CORE();
-        sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0);
-        LOCK_TCPIP_CORE();
-        LWIP_ASSERT("state!", msg->conn->state == NETCONN_NONE);
-      }
-#else /* LWIP_TCPIP_CORE_LOCKING */
-      lwip_netconn_do_close_internal(msg->conn);
-#endif /* LWIP_TCPIP_CORE_LOCKING */
-      /* for tcp netconns, lwip_netconn_do_close_internal ACKs the message */
-      return;
-    }
-  } else
-#endif /* LWIP_TCP */
-  {
-    msg->err = ERR_CONN;
-  }
-  TCPIP_APIMSG_ACK(msg);
-}
-
-#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD)
-/**
- * Join multicast groups for UDP netconns.
- * Called from netconn_join_leave_group
- *
- * @param m the api_msg pointing to the connection
- */
-void
-lwip_netconn_do_join_leave_group(void *m)
-{
-  struct api_msg *msg = (struct api_msg *)m;
-
-  msg->err = ERR_CONN;
-  if (msg->conn->pcb.tcp != NULL) {
-    if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) {
-#if LWIP_UDP
-#if LWIP_IPV6 && LWIP_IPV6_MLD
-      if (NETCONNTYPE_ISIPV6(msg->conn->type)) {
-        if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
-          msg->err = mld6_joingroup(ip_2_ip6(API_EXPR_REF(msg->msg.jl.netif_addr)),
-                                    ip_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr)));
-        } else {
-          msg->err = mld6_leavegroup(ip_2_ip6(API_EXPR_REF(msg->msg.jl.netif_addr)),
-                                     ip_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr)));
-        }
-      } else
-#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
-      {
-#if LWIP_IGMP
-        if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
-          msg->err = igmp_joingroup(ip_2_ip4(API_EXPR_REF(msg->msg.jl.netif_addr)),
-                                    ip_2_ip4(API_EXPR_REF(msg->msg.jl.multiaddr)));
-        } else {
-          msg->err = igmp_leavegroup(ip_2_ip4(API_EXPR_REF(msg->msg.jl.netif_addr)),
-                                     ip_2_ip4(API_EXPR_REF(msg->msg.jl.multiaddr)));
-        }
-#endif /* LWIP_IGMP */
-      }
-#endif /* LWIP_UDP */
-#if (LWIP_TCP || LWIP_RAW)
-    } else {
-      msg->err = ERR_VAL;
-#endif /* (LWIP_TCP || LWIP_RAW) */
-    }
-  }
-  TCPIP_APIMSG_ACK(msg);
-}
-/**
- * Join multicast groups for UDP netconns.
- * Called from netconn_join_leave_group_netif
- *
- * @param m the api_msg pointing to the connection
- */
-void
-lwip_netconn_do_join_leave_group_netif(void *m)
-{
-  struct api_msg *msg = (struct api_msg *)m;
-  struct netif *netif;
-
-  netif = netif_get_by_index(msg->msg.jl.if_idx);
-  if (netif == NULL) {
-    msg->err = ERR_IF;
-    goto done;
-  }
-
-  msg->err = ERR_CONN;
-  if (msg->conn->pcb.tcp != NULL) {
-    if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) {
-#if LWIP_UDP
-#if LWIP_IPV6 && LWIP_IPV6_MLD
-      if (NETCONNTYPE_ISIPV6(msg->conn->type)) {
-        if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
-          msg->err = mld6_joingroup_netif(netif,
-                                          ip_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr)));
-        } else {
-          msg->err = mld6_leavegroup_netif(netif,
-                                           ip_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr)));
-        }
-      } else
-#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
-      {
-#if LWIP_IGMP
-        if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
-          msg->err = igmp_joingroup_netif(netif,
-                                          ip_2_ip4(API_EXPR_REF(msg->msg.jl.multiaddr)));
-        } else {
-          msg->err = igmp_leavegroup_netif(netif,
-                                           ip_2_ip4(API_EXPR_REF(msg->msg.jl.multiaddr)));
-        }
-#endif /* LWIP_IGMP */
-      }
-#endif /* LWIP_UDP */
-#if (LWIP_TCP || LWIP_RAW)
-    } else {
-      msg->err = ERR_VAL;
-#endif /* (LWIP_TCP || LWIP_RAW) */
-    }
-  }
-
-done:
-  TCPIP_APIMSG_ACK(msg);
-}
-#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */
-
-#if LWIP_DNS
-/**
- * Callback function that is called when DNS name is resolved
- * (or on timeout). A waiting application thread is waked up by
- * signaling the semaphore.
- */
-static void
-lwip_netconn_do_dns_found(const char *name, const ip_addr_t *ipaddr, void *arg)
-{
-  struct dns_api_msg *msg = (struct dns_api_msg *)arg;
-
-  /* we trust the internal implementation to be correct :-) */
-  LWIP_UNUSED_ARG(name);
-
-  if (ipaddr == NULL) {
-    /* timeout or memory error */
-    API_EXPR_DEREF(msg->err) = ERR_VAL;
-  } else {
-    /* address was resolved */
-    API_EXPR_DEREF(msg->err) = ERR_OK;
-    API_EXPR_DEREF(msg->addr) = *ipaddr;
-  }
-  /* wake up the application task waiting in netconn_gethostbyname */
-  sys_sem_signal(API_EXPR_REF_SEM(msg->sem));
-}
-
-/**
- * Execute a DNS query
- * Called from netconn_gethostbyname
- *
- * @param arg the dns_api_msg pointing to the query
- */
-void
-lwip_netconn_do_gethostbyname(void *arg)
-{
-  struct dns_api_msg *msg = (struct dns_api_msg *)arg;
-  u8_t addrtype =
-#if LWIP_IPV4 && LWIP_IPV6
-    msg->dns_addrtype;
-#else
-    LWIP_DNS_ADDRTYPE_DEFAULT;
-#endif
-
-  API_EXPR_DEREF(msg->err) = dns_gethostbyname_addrtype(msg->name,
-                             API_EXPR_REF(msg->addr), lwip_netconn_do_dns_found, msg, addrtype);
-#if LWIP_TCPIP_CORE_LOCKING
-  /* For core locking, only block if we need to wait for answer/timeout */
-  if (API_EXPR_DEREF(msg->err) == ERR_INPROGRESS) {
-    UNLOCK_TCPIP_CORE();
-    sys_sem_wait(API_EXPR_REF_SEM(msg->sem));
-    LOCK_TCPIP_CORE();
-    LWIP_ASSERT("do_gethostbyname still in progress!!", API_EXPR_DEREF(msg->err) != ERR_INPROGRESS);
-  }
-#else /* LWIP_TCPIP_CORE_LOCKING */
-  if (API_EXPR_DEREF(msg->err) != ERR_INPROGRESS) {
-    /* on error or immediate success, wake up the application
-     * task waiting in netconn_gethostbyname */
-    sys_sem_signal(API_EXPR_REF_SEM(msg->sem));
-  }
-#endif /* LWIP_TCPIP_CORE_LOCKING */
-}
-#endif /* LWIP_DNS */
-
-#endif /* LWIP_NETCONN */
+/**
+ * @file
+ * Sequential API Internal module
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/priv/api_msg.h"
+
+#include "lwip/ip.h"
+#include "lwip/ip_addr.h"
+#include "lwip/udp.h"
+#include "lwip/tcp.h"
+#include "lwip/raw.h"
+
+#include "lwip/memp.h"
+#include "lwip/igmp.h"
+#include "lwip/dns.h"
+#include "lwip/mld6.h"
+#include "lwip/priv/tcpip_priv.h"
+
+#include <string.h>
+
+/* netconns are polled once per second (e.g. continue write on memory error) */
+#define NETCONN_TCP_POLL_INTERVAL 2
+
+#define SET_NONBLOCKING_CONNECT(conn, val)  do { if (val) { \
+  (conn)->flags |= NETCONN_FLAG_IN_NONBLOCKING_CONNECT; \
+} else { \
+  (conn)->flags &= ~ NETCONN_FLAG_IN_NONBLOCKING_CONNECT; }} while(0)
+#define IN_NONBLOCKING_CONNECT(conn) (((conn)->flags & NETCONN_FLAG_IN_NONBLOCKING_CONNECT) != 0)
+
+/* forward declarations */
+#if LWIP_TCP
+#if LWIP_TCPIP_CORE_LOCKING
+#define WRITE_DELAYED         , 1
+#define WRITE_DELAYED_PARAM   , u8_t delayed
+#else /* LWIP_TCPIP_CORE_LOCKING */
+#define WRITE_DELAYED
+#define WRITE_DELAYED_PARAM
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+static err_t lwip_netconn_do_writemore(struct netconn *conn  WRITE_DELAYED_PARAM);
+static err_t lwip_netconn_do_close_internal(struct netconn *conn  WRITE_DELAYED_PARAM);
+#endif
+
+#if LWIP_TCPIP_CORE_LOCKING
+#define TCPIP_APIMSG_ACK(m)   NETCONN_SET_SAFE_ERR((m)->conn, (m)->err)
+#else /* LWIP_TCPIP_CORE_LOCKING */
+#define TCPIP_APIMSG_ACK(m)   do { NETCONN_SET_SAFE_ERR((m)->conn, (m)->err); sys_sem_signal(LWIP_API_MSG_SEM(m)); } while(0)
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+
+#if LWIP_TCP
+u8_t netconn_aborted;
+#endif /* LWIP_TCP */
+
+#if LWIP_RAW
+/**
+ * Receive callback function for RAW netconns.
+ * Doesn't 'eat' the packet, only copies it and sends it to
+ * conn->recvmbox
+ *
+ * @see raw.h (struct raw_pcb.recv) for parameters and return value
+ */
+static u8_t
+recv_raw(void *arg, struct raw_pcb *pcb, struct pbuf *p,
+    const ip_addr_t *addr)
+{
+  struct pbuf *q;
+  struct netbuf *buf;
+  struct netconn *conn;
+
+  LWIP_UNUSED_ARG(addr);
+  conn = (struct netconn *)arg;
+
+  if ((conn != NULL) && sys_mbox_valid(&conn->recvmbox)) {
+#if LWIP_SO_RCVBUF
+    int recv_avail;
+    SYS_ARCH_GET(conn->recv_avail, recv_avail);
+    if ((recv_avail + (int)(p->tot_len)) > conn->recv_bufsize) {
+      return 0;
+    }
+#endif /* LWIP_SO_RCVBUF */
+    /* copy the whole packet into new pbufs */
+    q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM);
+    if (q != NULL) {
+      if (pbuf_copy(q, p) != ERR_OK) {
+        pbuf_free(q);
+        q = NULL;
+      }
+    }
+
+    if (q != NULL) {
+      u16_t len;
+      buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
+      if (buf == NULL) {
+        pbuf_free(q);
+        return 0;
+      }
+
+      buf->p = q;
+      buf->ptr = q;
+      ip_addr_copy(buf->addr, *ip_current_src_addr());
+      buf->port = pcb->protocol;
+
+      len = q->tot_len;
+      if (sys_mbox_trypost(&conn->recvmbox, buf) != ERR_OK) {
+        netbuf_delete(buf);
+        return 0;
+      } else {
+#if LWIP_SO_RCVBUF
+        SYS_ARCH_INC(conn->recv_avail, len);
+#endif /* LWIP_SO_RCVBUF */
+        /* Register event with callback */
+        API_EVENT(conn, NETCONN_EVT_RCVPLUS, len);
+      }
+    }
+  }
+
+  return 0; /* do not eat the packet */
+}
+#endif /* LWIP_RAW*/
+
+#if LWIP_UDP
+/**
+ * Receive callback function for UDP netconns.
+ * Posts the packet to conn->recvmbox or deletes it on memory error.
+ *
+ * @see udp.h (struct udp_pcb.recv) for parameters
+ */
+static void
+recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p,
+   const ip_addr_t *addr, u16_t port)
+{
+  struct netbuf *buf;
+  struct netconn *conn;
+  u16_t len;
+#if LWIP_SO_RCVBUF
+  int recv_avail;
+#endif /* LWIP_SO_RCVBUF */
+
+  LWIP_UNUSED_ARG(pcb); /* only used for asserts... */
+  LWIP_ASSERT("recv_udp must have a pcb argument", pcb != NULL);
+  LWIP_ASSERT("recv_udp must have an argument", arg != NULL);
+  conn = (struct netconn *)arg;
+  LWIP_ASSERT("recv_udp: recv for wrong pcb!", conn->pcb.udp == pcb);
+
+#if LWIP_SO_RCVBUF
+  SYS_ARCH_GET(conn->recv_avail, recv_avail);
+  if ((conn == NULL) || !sys_mbox_valid(&conn->recvmbox) ||
+      ((recv_avail + (int)(p->tot_len)) > conn->recv_bufsize)) {
+#else  /* LWIP_SO_RCVBUF */
+  if ((conn == NULL) || !sys_mbox_valid(&conn->recvmbox)) {
+#endif /* LWIP_SO_RCVBUF */
+    pbuf_free(p);
+    return;
+  }
+
+  buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
+  if (buf == NULL) {
+    pbuf_free(p);
+    return;
+  } else {
+    buf->p = p;
+    buf->ptr = p;
+    ip_addr_set(&buf->addr, addr);
+    buf->port = port;
+#if LWIP_NETBUF_RECVINFO
+    {
+      /* get the UDP header - always in the first pbuf, ensured by udp_input */
+      const struct udp_hdr* udphdr = (const struct udp_hdr*)ip_next_header_ptr();
+#if LWIP_CHECKSUM_ON_COPY
+      buf->flags = NETBUF_FLAG_DESTADDR;
+#endif /* LWIP_CHECKSUM_ON_COPY */
+      ip_addr_set(&buf->toaddr, ip_current_dest_addr());
+      buf->toport_chksum = udphdr->dest;
+    }
+#endif /* LWIP_NETBUF_RECVINFO */
+  }
+
+  len = p->tot_len;
+  if (sys_mbox_trypost(&conn->recvmbox, buf) != ERR_OK) {
+    netbuf_delete(buf);
+    return;
+  } else {
+#if LWIP_SO_RCVBUF
+    SYS_ARCH_INC(conn->recv_avail, len);
+#endif /* LWIP_SO_RCVBUF */
+    /* Register event with callback */
+    API_EVENT(conn, NETCONN_EVT_RCVPLUS, len);
+  }
+}
+#endif /* LWIP_UDP */
+
+#if LWIP_TCP
+/**
+ * Receive callback function for TCP netconns.
+ * Posts the packet to conn->recvmbox, but doesn't delete it on errors.
+ *
+ * @see tcp.h (struct tcp_pcb.recv) for parameters and return value
+ */
+static err_t
+recv_tcp(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
+{
+  struct netconn *conn;
+  u16_t len;
+
+  LWIP_UNUSED_ARG(pcb);
+  LWIP_ASSERT("recv_tcp must have a pcb argument", pcb != NULL);
+  LWIP_ASSERT("recv_tcp must have an argument", arg != NULL);
+  conn = (struct netconn *)arg;
+
+  if (conn == NULL) {
+    return ERR_VAL;
+  }
+  LWIP_ASSERT("recv_tcp: recv for wrong pcb!", conn->pcb.tcp == pcb);
+
+  if (!sys_mbox_valid(&conn->recvmbox)) {
+    /* recvmbox already deleted */
+    if (p != NULL) {
+      tcp_recved(pcb, p->tot_len);
+      pbuf_free(p);
+    }
+    return ERR_OK;
+  }
+  /* Unlike for UDP or RAW pcbs, don't check for available space
+     using recv_avail since that could break the connection
+     (data is already ACKed) */
+
+  /* don't overwrite fatal errors! */
+  if (err != ERR_OK) {
+    NETCONN_SET_SAFE_ERR(conn, err);
+  }
+
+  if (p != NULL) {
+    len = p->tot_len;
+  } else {
+    len = 0;
+  }
+
+  if (sys_mbox_trypost(&conn->recvmbox, p) != ERR_OK) {
+    /* don't deallocate p: it is presented to us later again from tcp_fasttmr! */
+    return ERR_MEM;
+  } else {
+#if LWIP_SO_RCVBUF
+    SYS_ARCH_INC(conn->recv_avail, len);
+#endif /* LWIP_SO_RCVBUF */
+    /* Register event with callback */
+    API_EVENT(conn, NETCONN_EVT_RCVPLUS, len);
+  }
+
+  return ERR_OK;
+}
+
+/**
+ * Poll callback function for TCP netconns.
+ * Wakes up an application thread that waits for a connection to close
+ * or data to be sent. The application thread then takes the
+ * appropriate action to go on.
+ *
+ * Signals the conn->sem.
+ * netconn_close waits for conn->sem if closing failed.
+ *
+ * @see tcp.h (struct tcp_pcb.poll) for parameters and return value
+ */
+static err_t
+poll_tcp(void *arg, struct tcp_pcb *pcb)
+{
+  struct netconn *conn = (struct netconn *)arg;
+
+  LWIP_UNUSED_ARG(pcb);
+  LWIP_ASSERT("conn != NULL", (conn != NULL));
+
+  if (conn->state == NETCONN_WRITE) {
+    lwip_netconn_do_writemore(conn  WRITE_DELAYED);
+  } else if (conn->state == NETCONN_CLOSE) {
+#if !LWIP_SO_SNDTIMEO && !LWIP_SO_LINGER
+    if (conn->current_msg && conn->current_msg->msg.sd.polls_left) {
+      conn->current_msg->msg.sd.polls_left--;
+    }
+#endif /* !LWIP_SO_SNDTIMEO && !LWIP_SO_LINGER */
+    lwip_netconn_do_close_internal(conn  WRITE_DELAYED);
+  }
+  /* @todo: implement connect timeout here? */
+
+  /* Did a nonblocking write fail before? Then check available write-space. */
+  if (conn->flags & NETCONN_FLAG_CHECK_WRITESPACE) {
+    /* If the queued byte- or pbuf-count drops below the configured low-water limit,
+       let select mark this pcb as writable again. */
+    if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT) &&
+      (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT)) {
+      conn->flags &= ~NETCONN_FLAG_CHECK_WRITESPACE;
+      API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
+    }
+  }
+
+  return ERR_OK;
+}
+
+/**
+ * Sent callback function for TCP netconns.
+ * Signals the conn->sem and calls API_EVENT.
+ * netconn_write waits for conn->sem if send buffer is low.
+ *
+ * @see tcp.h (struct tcp_pcb.sent) for parameters and return value
+ */
+static err_t
+sent_tcp(void *arg, struct tcp_pcb *pcb, u16_t len)
+{
+  struct netconn *conn = (struct netconn *)arg;
+
+  LWIP_UNUSED_ARG(pcb);
+  LWIP_ASSERT("conn != NULL", (conn != NULL));
+
+  if (conn) {
+    if (conn->state == NETCONN_WRITE) {
+      lwip_netconn_do_writemore(conn  WRITE_DELAYED);
+    } else if (conn->state == NETCONN_CLOSE) {
+      lwip_netconn_do_close_internal(conn  WRITE_DELAYED);
+    }
+
+    /* If the queued byte- or pbuf-count drops below the configured low-water limit,
+       let select mark this pcb as writable again. */
+    if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT) &&
+      (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT)) {
+      conn->flags &= ~NETCONN_FLAG_CHECK_WRITESPACE;
+      API_EVENT(conn, NETCONN_EVT_SENDPLUS, len);
+    }
+  }
+
+  return ERR_OK;
+}
+
+/**
+ * Error callback function for TCP netconns.
+ * Signals conn->sem, posts to all conn mboxes and calls API_EVENT.
+ * The application thread has then to decide what to do.
+ *
+ * @see tcp.h (struct tcp_pcb.err) for parameters
+ */
+static void
+err_tcp(void *arg, err_t err)
+{
+  struct netconn *conn;
+  enum netconn_state old_state;
+
+  conn = (struct netconn *)arg;
+  LWIP_ASSERT("conn != NULL", (conn != NULL));
+
+  conn->pcb.tcp = NULL;
+
+  /* reset conn->state now before waking up other threads */
+  old_state = conn->state;
+  conn->state = NETCONN_NONE;
+
+  if (old_state == NETCONN_CLOSE) {
+    /* RST during close: let close return success & dealloc the netconn */
+    err = ERR_OK;
+    NETCONN_SET_SAFE_ERR(conn, ERR_OK);
+  } else {
+    /* no check since this is always fatal! */
+    SYS_ARCH_SET(conn->last_err, err);
+  }
+
+  /* @todo: the type of NETCONN_EVT created should depend on 'old_state' */
+
+  /* Notify the user layer about a connection error. Used to signal select. */
+  API_EVENT(conn, NETCONN_EVT_ERROR, 0);
+  /* Try to release selects pending on 'read' or 'write', too.
+     They will get an error if they actually try to read or write. */
+  API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
+  API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
+
+  /* pass NULL-message to recvmbox to wake up pending recv */
+  if (sys_mbox_valid(&conn->recvmbox)) {
+    /* use trypost to prevent deadlock */
+    sys_mbox_trypost(&conn->recvmbox, NULL);
+  }
+  /* pass NULL-message to acceptmbox to wake up pending accept */
+  if (sys_mbox_valid(&conn->acceptmbox)) {
+    /* use trypost to preven deadlock */
+    sys_mbox_trypost(&conn->acceptmbox, NULL);
+  }
+
+  if ((old_state == NETCONN_WRITE) || (old_state == NETCONN_CLOSE) ||
+      (old_state == NETCONN_CONNECT)) {
+    /* calling lwip_netconn_do_writemore/lwip_netconn_do_close_internal is not necessary
+       since the pcb has already been deleted! */
+    int was_nonblocking_connect = IN_NONBLOCKING_CONNECT(conn);
+    SET_NONBLOCKING_CONNECT(conn, 0);
+
+    if (!was_nonblocking_connect) {
+      sys_sem_t* op_completed_sem;
+      /* set error return code */
+      LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
+      conn->current_msg->err = err;
+      op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
+      LWIP_ASSERT("inavlid op_completed_sem", sys_sem_valid(op_completed_sem));
+      conn->current_msg = NULL;
+      /* wake up the waiting task */
+      NETCONN_SET_SAFE_ERR(conn, err);
+      sys_sem_signal(op_completed_sem);
+    }
+  } else {
+    LWIP_ASSERT("conn->current_msg == NULL", conn->current_msg == NULL);
+  }
+}
+
+/**
+ * Setup a tcp_pcb with the correct callback function pointers
+ * and their arguments.
+ *
+ * @param conn the TCP netconn to setup
+ */
+static void
+setup_tcp(struct netconn *conn)
+{
+  struct tcp_pcb *pcb;
+
+  pcb = conn->pcb.tcp;
+  tcp_arg(pcb, conn);
+  tcp_recv(pcb, recv_tcp);
+  tcp_sent(pcb, sent_tcp);
+  tcp_poll(pcb, poll_tcp, NETCONN_TCP_POLL_INTERVAL);
+  tcp_err(pcb, err_tcp);
+}
+
+/**
+ * Accept callback function for TCP netconns.
+ * Allocates a new netconn and posts that to conn->acceptmbox.
+ *
+ * @see tcp.h (struct tcp_pcb_listen.accept) for parameters and return value
+ */
+static err_t
+accept_function(void *arg, struct tcp_pcb *newpcb, err_t err)
+{
+  struct netconn *newconn;
+  struct netconn *conn = (struct netconn *)arg;
+
+  LWIP_DEBUGF(API_MSG_DEBUG, ("accept_function: newpcb->tate: %s\n", tcp_debug_state_str(newpcb->state)));
+
+  if (conn == NULL) {
+    return ERR_VAL;
+  }
+  if (!sys_mbox_valid(&conn->acceptmbox)) {
+    LWIP_DEBUGF(API_MSG_DEBUG, ("accept_function: acceptmbox already deleted\n"));
+    return ERR_VAL;
+  }
+
+  if (newpcb == NULL) {
+    /* out-of-pcbs during connect: pass on this error to the application */
+    if (sys_mbox_trypost(&conn->acceptmbox, &netconn_aborted) == ERR_OK) {
+      /* Register event with callback */
+      API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
+    }
+    return ERR_VAL;
+  }
+
+  /* We have to set the callback here even though
+   * the new socket is unknown. newconn->socket is marked as -1. */
+  newconn = netconn_alloc(conn->type, conn->callback);
+  if (newconn == NULL) {
+    /* outof netconns: pass on this error to the application */
+    if (sys_mbox_trypost(&conn->acceptmbox, &netconn_aborted) == ERR_OK) {
+      /* Register event with callback */
+      API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
+    }
+    return ERR_MEM;
+  }
+  newconn->pcb.tcp = newpcb;
+  setup_tcp(newconn);
+  /* no protection: when creating the pcb, the netconn is not yet known
+     to the application thread */
+  newconn->last_err = err;
+
+  /* handle backlog counter */
+  tcp_backlog_delayed(newpcb);
+
+  if (sys_mbox_trypost(&conn->acceptmbox, newconn) != ERR_OK) {
+    /* When returning != ERR_OK, the pcb is aborted in tcp_process(),
+       so do nothing here! */
+    /* remove all references to this netconn from the pcb */
+    struct tcp_pcb* pcb = newconn->pcb.tcp;
+    tcp_arg(pcb, NULL);
+    tcp_recv(pcb, NULL);
+    tcp_sent(pcb, NULL);
+    tcp_poll(pcb, NULL, 0);
+    tcp_err(pcb, NULL);
+    /* remove reference from to the pcb from this netconn */
+    newconn->pcb.tcp = NULL;
+    /* no need to drain since we know the recvmbox is empty. */
+    sys_mbox_free(&newconn->recvmbox);
+    sys_mbox_set_invalid(&newconn->recvmbox);
+    netconn_free(newconn);
+    return ERR_MEM;
+  } else {
+    /* Register event with callback */
+    API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
+  }
+
+  return ERR_OK;
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Create a new pcb of a specific type.
+ * Called from lwip_netconn_do_newconn().
+ *
+ * @param msg the api_msg_msg describing the connection type
+ */
+static void
+pcb_new(struct api_msg *msg)
+{
+  enum lwip_ip_addr_type iptype = IPADDR_TYPE_V4;
+
+  LWIP_ASSERT("pcb_new: pcb already allocated", msg->conn->pcb.tcp == NULL);
+ 
+#if LWIP_IPV6 && LWIP_IPV4
+  /* IPv6: Dual-stack by default, unless netconn_set_ipv6only() is called */
+  if(NETCONNTYPE_ISIPV6(netconn_type(msg->conn))) {
+    iptype = IPADDR_TYPE_ANY;
+  }
+#endif
+  
+  /* Allocate a PCB for this connection */
+  switch(NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+  case NETCONN_RAW:
+    msg->conn->pcb.raw = raw_new_ip_type(iptype, msg->msg.n.proto);
+    if (msg->conn->pcb.raw != NULL) {
+#if LWIP_IPV6
+      /* ICMPv6 packets should always have checksum calculated by the stack as per RFC 3542 chapter 3.1 */
+      if (NETCONNTYPE_ISIPV6(msg->conn->type) && msg->conn->pcb.raw->protocol == IP6_NEXTH_ICMP6) {
+        msg->conn->pcb.raw->chksum_reqd = 1;
+        msg->conn->pcb.raw->chksum_offset = 2;
+      }
+#endif /* LWIP_IPV6 */
+      raw_recv(msg->conn->pcb.raw, recv_raw, msg->conn);
+    }
+    break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+  case NETCONN_UDP:
+    msg->conn->pcb.udp = udp_new_ip_type(iptype);
+    if (msg->conn->pcb.udp != NULL) {
+#if LWIP_UDPLITE
+      if (NETCONNTYPE_ISUDPLITE(msg->conn->type)) {
+        udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_UDPLITE);
+      }
+#endif /* LWIP_UDPLITE */
+      if (NETCONNTYPE_ISUDPNOCHKSUM(msg->conn->type)) {
+        udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_NOCHKSUM);
+      }
+      udp_recv(msg->conn->pcb.udp, recv_udp, msg->conn);
+    }
+    break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+  case NETCONN_TCP:
+    msg->conn->pcb.tcp = tcp_new_ip_type(iptype);
+    if (msg->conn->pcb.tcp != NULL) {
+      setup_tcp(msg->conn);
+    }
+    break;
+#endif /* LWIP_TCP */
+  default:
+    /* Unsupported netconn type, e.g. protocol disabled */
+    msg->err = ERR_VAL;
+    return;
+  }
+  if (msg->conn->pcb.ip == NULL) {
+    msg->err = ERR_MEM;
+  }
+}
+
+/**
+ * Create a new pcb of a specific type inside a netconn.
+ * Called from netconn_new_with_proto_and_callback.
+ *
+ * @param m the api_msg_msg describing the connection type
+ */
+void
+lwip_netconn_do_newconn(void *m)
+{
+  struct api_msg *msg = (struct api_msg*)m;
+
+  msg->err = ERR_OK;
+  if (msg->conn->pcb.tcp == NULL) {
+    pcb_new(msg);
+  }
+  /* Else? This "new" connection already has a PCB allocated. */
+  /* Is this an error condition? Should it be deleted? */
+  /* We currently just are happy and return. */
+
+  TCPIP_APIMSG_ACK(msg);
+}
+
+/**
+ * Create a new netconn (of a specific type) that has a callback function.
+ * The corresponding pcb is NOT created!
+ *
+ * @param t the type of 'connection' to create (@see enum netconn_type)
+ * @param callback a function to call on status changes (RX available, TX'ed)
+ * @return a newly allocated struct netconn or
+ *         NULL on memory error
+ */
+struct netconn*
+netconn_alloc(enum netconn_type t, netconn_callback callback)
+{
+  struct netconn *conn;
+  int size;
+
+  conn = (struct netconn *)memp_malloc(MEMP_NETCONN);
+  if (conn == NULL) {
+    return NULL;
+  }
+
+  conn->last_err = ERR_OK;
+  conn->type = t;
+  conn->pcb.tcp = NULL;
+
+  /* If all sizes are the same, every compiler should optimize this switch to nothing */
+  switch(NETCONNTYPE_GROUP(t)) {
+#if LWIP_RAW
+  case NETCONN_RAW:
+    size = DEFAULT_RAW_RECVMBOX_SIZE;
+    break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+  case NETCONN_UDP:
+    size = DEFAULT_UDP_RECVMBOX_SIZE;
+    break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+  case NETCONN_TCP:
+    size = DEFAULT_TCP_RECVMBOX_SIZE;
+    break;
+#endif /* LWIP_TCP */
+  default:
+    LWIP_ASSERT("netconn_alloc: undefined netconn_type", 0);
+    goto free_and_return;
+  }
+
+  if (sys_mbox_new(&conn->recvmbox, size) != ERR_OK) {
+    goto free_and_return;
+  }
+#if !LWIP_NETCONN_SEM_PER_THREAD
+  if (sys_sem_new(&conn->op_completed, 0) != ERR_OK) {
+    sys_mbox_free(&conn->recvmbox);
+    goto free_and_return;
+  }
+#endif
+
+#if LWIP_TCP
+  sys_mbox_set_invalid(&conn->acceptmbox);
+#endif
+  conn->state        = NETCONN_NONE;
+#if LWIP_SOCKET
+  /* initialize socket to -1 since 0 is a valid socket */
+  conn->socket       = -1;
+#endif /* LWIP_SOCKET */
+  conn->callback     = callback;
+#if LWIP_TCP
+  conn->current_msg  = NULL;
+  conn->write_offset = 0;
+#endif /* LWIP_TCP */
+#if LWIP_SO_SNDTIMEO
+  conn->send_timeout = 0;
+#endif /* LWIP_SO_SNDTIMEO */
+#if LWIP_SO_RCVTIMEO
+  conn->recv_timeout = 0;
+#endif /* LWIP_SO_RCVTIMEO */
+#if LWIP_SO_RCVBUF
+  conn->recv_bufsize = RECV_BUFSIZE_DEFAULT;
+  conn->recv_avail   = 0;
+#endif /* LWIP_SO_RCVBUF */
+#if LWIP_SO_LINGER
+  conn->linger = -1;
+#endif /* LWIP_SO_LINGER */
+  conn->flags = 0;
+  return conn;
+free_and_return:
+  memp_free(MEMP_NETCONN, conn);
+  return NULL;
+}
+
+/**
+ * Delete a netconn and all its resources.
+ * The pcb is NOT freed (since we might not be in the right thread context do this).
+ *
+ * @param conn the netconn to free
+ */
+void
+netconn_free(struct netconn *conn)
+{
+  LWIP_ASSERT("PCB must be deallocated outside this function", conn->pcb.tcp == NULL);
+  LWIP_ASSERT("recvmbox must be deallocated before calling this function",
+    !sys_mbox_valid(&conn->recvmbox));
+#if LWIP_TCP
+  LWIP_ASSERT("acceptmbox must be deallocated before calling this function",
+    !sys_mbox_valid(&conn->acceptmbox));
+#endif /* LWIP_TCP */
+
+#if !LWIP_NETCONN_SEM_PER_THREAD
+  sys_sem_free(&conn->op_completed);
+  sys_sem_set_invalid(&conn->op_completed);
+#endif
+
+  memp_free(MEMP_NETCONN, conn);
+}
+
+/**
+ * Delete rcvmbox and acceptmbox of a netconn and free the left-over data in
+ * these mboxes
+ *
+ * @param conn the netconn to free
+ * @bytes_drained bytes drained from recvmbox
+ * @accepts_drained pending connections drained from acceptmbox
+ */
+static void
+netconn_drain(struct netconn *conn)
+{
+  void *mem;
+#if LWIP_TCP
+  struct pbuf *p;
+#endif /* LWIP_TCP */
+
+  /* This runs in tcpip_thread, so we don't need to lock against rx packets */
+
+  /* Delete and drain the recvmbox. */
+  if (sys_mbox_valid(&conn->recvmbox)) {
+    while (sys_mbox_tryfetch(&conn->recvmbox, &mem) != SYS_MBOX_EMPTY) {
+#if LWIP_TCP
+      if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) {
+        if (mem != NULL) {
+          p = (struct pbuf*)mem;
+          /* pcb might be set to NULL already by err_tcp() */
+          if (conn->pcb.tcp != NULL) {
+            tcp_recved(conn->pcb.tcp, p->tot_len);
+          }
+          pbuf_free(p);
+        }
+      } else
+#endif /* LWIP_TCP */
+      {
+        netbuf_delete((struct netbuf *)mem);
+      }
+    }
+    sys_mbox_free(&conn->recvmbox);
+    sys_mbox_set_invalid(&conn->recvmbox);
+  }
+
+  /* Delete and drain the acceptmbox. */
+#if LWIP_TCP
+  if (sys_mbox_valid(&conn->acceptmbox)) {
+    while (sys_mbox_tryfetch(&conn->acceptmbox, &mem) != SYS_MBOX_EMPTY) {
+      if (mem != &netconn_aborted) {
+        struct netconn *newconn = (struct netconn *)mem;
+        /* Only tcp pcbs have an acceptmbox, so no need to check conn->type */
+        /* pcb might be set to NULL already by err_tcp() */
+        /* drain recvmbox */
+        netconn_drain(newconn);
+        if (newconn->pcb.tcp != NULL) {
+          tcp_abort(newconn->pcb.tcp);
+          newconn->pcb.tcp = NULL;
+        }
+        netconn_free(newconn);
+      }
+    }
+    sys_mbox_free(&conn->acceptmbox);
+    sys_mbox_set_invalid(&conn->acceptmbox);
+  }
+#endif /* LWIP_TCP */
+}
+
+#if LWIP_TCP
+/**
+ * Internal helper function to close a TCP netconn: since this sometimes
+ * doesn't work at the first attempt, this function is called from multiple
+ * places.
+ *
+ * @param conn the TCP netconn to close
+ */
+static err_t
+lwip_netconn_do_close_internal(struct netconn *conn  WRITE_DELAYED_PARAM)
+{
+  err_t err;
+  u8_t shut, shut_rx, shut_tx, close;
+  u8_t close_finished = 0;
+  struct tcp_pcb* tpcb;
+#if LWIP_SO_LINGER
+  u8_t linger_wait_required = 0;
+#endif /* LWIP_SO_LINGER */
+
+  LWIP_ASSERT("invalid conn", (conn != NULL));
+  LWIP_ASSERT("this is for tcp netconns only", (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP));
+  LWIP_ASSERT("conn must be in state NETCONN_CLOSE", (conn->state == NETCONN_CLOSE));
+  LWIP_ASSERT("pcb already closed", (conn->pcb.tcp != NULL));
+  LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
+
+  tpcb = conn->pcb.tcp;
+  shut = conn->current_msg->msg.sd.shut;
+  shut_rx = shut & NETCONN_SHUT_RD;
+  shut_tx = shut & NETCONN_SHUT_WR;
+  /* shutting down both ends is the same as closing
+     (also if RD or WR side was shut down before already) */
+  if (shut == NETCONN_SHUT_RDWR) {
+    close = 1;
+  } else if (shut_rx &&
+             ((tpcb->state == FIN_WAIT_1) ||
+              (tpcb->state == FIN_WAIT_2) ||
+              (tpcb->state == CLOSING))) {
+    close = 1;
+  } else if (shut_tx && ((tpcb->flags & TF_RXCLOSED) != 0)) {
+    close = 1;
+  } else {
+    close = 0;
+  }
+
+  /* Set back some callback pointers */
+  if (close) {
+    tcp_arg(tpcb, NULL);
+  }
+  if (tpcb->state == LISTEN) {
+    tcp_accept(tpcb, NULL);
+  } else {
+    /* some callbacks have to be reset if tcp_close is not successful */
+    if (shut_rx) {
+      tcp_recv(tpcb, NULL);
+      tcp_accept(tpcb, NULL);
+    }
+    if (shut_tx) {
+      tcp_sent(tpcb, NULL);
+    }
+    if (close) {
+      tcp_poll(tpcb, NULL, 0);
+      tcp_err(tpcb, NULL);
+    }
+  }
+  /* Try to close the connection */
+  if (close) {
+#if LWIP_SO_LINGER
+    /* check linger possibilites before calling tcp_close */
+    err = ERR_OK;
+    /* linger enabled/required at all? (i.e. is there untransmitted data left?) */
+    if ((conn->linger >= 0) && (conn->pcb.tcp->unsent || conn->pcb.tcp->unacked)) {
+      if ((conn->linger == 0)) {
+        /* data left but linger prevents waiting */
+        tcp_abort(tpcb);
+        tpcb = NULL;
+      } else if (conn->linger > 0) {
+        /* data left and linger says we should wait */
+        if (netconn_is_nonblocking(conn)) {
+          /* data left on a nonblocking netconn -> cannot linger */
+          err = ERR_WOULDBLOCK;
+        } else if ((s32_t)(sys_now() - conn->current_msg->msg.sd.time_started) >=
+          (conn->linger * 1000)) {
+          /* data left but linger timeout has expired (this happens on further
+             calls to this function through poll_tcp */
+          tcp_abort(tpcb);
+          tpcb = NULL;
+        } else {
+          /* data left -> need to wait for ACK after successful close */
+          linger_wait_required = 1;
+        }
+      }
+    }
+    if ((err == ERR_OK) && (tpcb != NULL))
+#endif /* LWIP_SO_LINGER */
+    {
+      err = tcp_close(tpcb);
+    }
+  } else {
+    err = tcp_shutdown(tpcb, shut_rx, shut_tx);
+  }
+  if (err == ERR_OK) {
+    close_finished = 1;
+#if LWIP_SO_LINGER
+    if (linger_wait_required) {
+      /* wait for ACK of all unsent/unacked data by just getting called again */
+      close_finished = 0;
+      err = ERR_INPROGRESS;
+    }
+#endif /* LWIP_SO_LINGER */
+  } else {
+    if (err == ERR_MEM) {
+      /* Closing failed because of memory shortage, try again later. Even for
+         nonblocking netconns, we have to wait since no standard socket application
+         is prepared for close failing because of resource shortage.
+         Check the timeout: this is kind of an lwip addition to the standard sockets:
+         we wait for some time when failing to allocate a segment for the FIN */
+#if LWIP_SO_SNDTIMEO || LWIP_SO_LINGER
+      s32_t close_timeout = LWIP_TCP_CLOSE_TIMEOUT_MS_DEFAULT;
+#if LWIP_SO_SNDTIMEO
+      if (conn->send_timeout > 0) {
+        close_timeout = conn->send_timeout;
+      }
+#endif /* LWIP_SO_SNDTIMEO */
+#if LWIP_SO_LINGER
+      if (conn->linger >= 0) {
+        /* use linger timeout (seconds) */
+        close_timeout = conn->linger * 1000U;
+      }
+#endif
+      if ((s32_t)(sys_now() - conn->current_msg->msg.sd.time_started) >= close_timeout) {
+#else /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
+      if (conn->current_msg->msg.sd.polls_left == 0) {
+#endif /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
+        close_finished = 1;
+        if (close) {
+          /* in this case, we want to RST the connection */
+          tcp_abort(tpcb);
+          err = ERR_OK;
+        }
+      }
+    } else {
+      /* Closing failed for a non-memory error: give up */
+      close_finished = 1;
+    }
+  }
+  if (close_finished) {
+    /* Closing done (succeeded, non-memory error, nonblocking error or timeout) */
+    sys_sem_t* op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
+    conn->current_msg->err = err;
+    conn->current_msg = NULL;
+    conn->state = NETCONN_NONE;
+    if (err == ERR_OK) {
+      if (close) {
+        /* Set back some callback pointers as conn is going away */
+        conn->pcb.tcp = NULL;
+        /* Trigger select() in socket layer. Make sure everybody notices activity
+         on the connection, error first! */
+        API_EVENT(conn, NETCONN_EVT_ERROR, 0);
+      }
+      if (shut_rx) {
+        API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
+      }
+      if (shut_tx) {
+        API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
+      }
+    }
+    NETCONN_SET_SAFE_ERR(conn, err);
+#if LWIP_TCPIP_CORE_LOCKING
+    if (delayed)
+#endif
+    {
+      /* wake up the application task */
+      sys_sem_signal(op_completed_sem);
+    }
+    return ERR_OK;
+  }
+  if (!close_finished) {
+    /* Closing failed and we want to wait: restore some of the callbacks */
+    /* Closing of listen pcb will never fail! */
+    LWIP_ASSERT("Closing a listen pcb may not fail!", (tpcb->state != LISTEN));
+    if (shut_tx) {
+      tcp_sent(tpcb, sent_tcp);
+    }
+    /* when waiting for close, set up poll interval to 500ms */
+    tcp_poll(tpcb, poll_tcp, 1);
+    tcp_err(tpcb, err_tcp);
+    tcp_arg(tpcb, conn);
+    /* don't restore recv callback: we don't want to receive any more data */
+  }
+  /* If closing didn't succeed, we get called again either
+     from poll_tcp or from sent_tcp */
+  LWIP_ASSERT("err != ERR_OK", err != ERR_OK);
+  return err;
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Delete the pcb inside a netconn.
+ * Called from netconn_delete.
+ *
+ * @param m the api_msg_msg pointing to the connection
+ */
+void
+lwip_netconn_do_delconn(void *m)
+{
+  struct api_msg *msg = (struct api_msg*)m;
+
+  enum netconn_state state = msg->conn->state;
+  LWIP_ASSERT("netconn state error", /* this only happens for TCP netconns */
+    (state == NETCONN_NONE) || (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP));
+#if LWIP_NETCONN_FULLDUPLEX
+  /* In full duplex mode, blocking write/connect is aborted with ERR_CLSD */
+  if (state != NETCONN_NONE) {
+    if ((state == NETCONN_WRITE) ||
+        ((state == NETCONN_CONNECT) && !IN_NONBLOCKING_CONNECT(msg->conn))) {
+      /* close requested, abort running write/connect */
+      sys_sem_t* op_completed_sem;
+      LWIP_ASSERT("msg->conn->current_msg != NULL", msg->conn->current_msg != NULL);
+      op_completed_sem = LWIP_API_MSG_SEM(msg->conn->current_msg);
+      msg->conn->current_msg->err = ERR_CLSD;
+      msg->conn->current_msg = NULL;
+      msg->conn->write_offset = 0;
+      msg->conn->state = NETCONN_NONE;
+      NETCONN_SET_SAFE_ERR(msg->conn, ERR_CLSD);
+      sys_sem_signal(op_completed_sem);
+    }
+  }
+#else /* LWIP_NETCONN_FULLDUPLEX */
+  if (((state != NETCONN_NONE) &&
+       (state != NETCONN_LISTEN) &&
+       (state != NETCONN_CONNECT)) ||
+      ((state == NETCONN_CONNECT) && !IN_NONBLOCKING_CONNECT(msg->conn))) {
+    /* This means either a blocking write or blocking connect is running
+       (nonblocking write returns and sets state to NONE) */
+    msg->err = ERR_INPROGRESS;
+  } else
+#endif /* LWIP_NETCONN_FULLDUPLEX */
+  {
+    LWIP_ASSERT("blocking connect in progress",
+      (state != NETCONN_CONNECT) || IN_NONBLOCKING_CONNECT(msg->conn));
+    msg->err = ERR_OK;
+    /* Drain and delete mboxes */
+    netconn_drain(msg->conn);
+
+    if (msg->conn->pcb.tcp != NULL) {
+
+      switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+      case NETCONN_RAW:
+        raw_remove(msg->conn->pcb.raw);
+        break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+      case NETCONN_UDP:
+        msg->conn->pcb.udp->recv_arg = NULL;
+        udp_remove(msg->conn->pcb.udp);
+        break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+      case NETCONN_TCP:
+        LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL &&
+          msg->conn->write_offset == 0);
+        msg->conn->state = NETCONN_CLOSE;
+        msg->msg.sd.shut = NETCONN_SHUT_RDWR;
+        msg->conn->current_msg = msg;
+#if LWIP_TCPIP_CORE_LOCKING
+        if (lwip_netconn_do_close_internal(msg->conn, 0) != ERR_OK) {
+          LWIP_ASSERT("state!", msg->conn->state == NETCONN_CLOSE);
+          UNLOCK_TCPIP_CORE();
+          sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0);
+          LOCK_TCPIP_CORE();
+          LWIP_ASSERT("state!", msg->conn->state == NETCONN_NONE);
+        }
+#else /* LWIP_TCPIP_CORE_LOCKING */
+        lwip_netconn_do_close_internal(msg->conn);
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+        /* API_EVENT is called inside lwip_netconn_do_close_internal, before releasing
+           the application thread, so we can return at this point! */
+        return;
+#endif /* LWIP_TCP */
+      default:
+        break;
+      }
+      msg->conn->pcb.tcp = NULL;
+    }
+    /* tcp netconns don't come here! */
+
+    /* @todo: this lets select make the socket readable and writable,
+       which is wrong! errfd instead? */
+    API_EVENT(msg->conn, NETCONN_EVT_RCVPLUS, 0);
+    API_EVENT(msg->conn, NETCONN_EVT_SENDPLUS, 0);
+  }
+  if (sys_sem_valid(LWIP_API_MSG_SEM(msg))) {
+    TCPIP_APIMSG_ACK(msg);
+  }
+}
+
+/**
+ * Bind a pcb contained in a netconn
+ * Called from netconn_bind.
+ *
+ * @param m the api_msg_msg pointing to the connection and containing
+ *          the IP address and port to bind to
+ */
+void
+lwip_netconn_do_bind(void *m)
+{
+  struct api_msg *msg = (struct api_msg*)m;
+
+  if (ERR_IS_FATAL(msg->conn->last_err)) {
+    msg->err = msg->conn->last_err;
+  } else {
+    msg->err = ERR_VAL;
+    if (msg->conn->pcb.tcp != NULL) {
+      switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+      case NETCONN_RAW:
+        msg->err = raw_bind(msg->conn->pcb.raw, API_EXPR_REF(msg->msg.bc.ipaddr));
+        break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+      case NETCONN_UDP:
+        msg->err = udp_bind(msg->conn->pcb.udp, API_EXPR_REF(msg->msg.bc.ipaddr), msg->msg.bc.port);
+        break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+      case NETCONN_TCP:
+        msg->err = tcp_bind(msg->conn->pcb.tcp, API_EXPR_REF(msg->msg.bc.ipaddr), msg->msg.bc.port);
+        break;
+#endif /* LWIP_TCP */
+      default:
+        break;
+      }
+    }
+  }
+  TCPIP_APIMSG_ACK(msg);
+}
+
+#if LWIP_TCP
+/**
+ * TCP callback function if a connection (opened by tcp_connect/lwip_netconn_do_connect) has
+ * been established (or reset by the remote host).
+ *
+ * @see tcp.h (struct tcp_pcb.connected) for parameters and return values
+ */
+static err_t
+lwip_netconn_do_connected(void *arg, struct tcp_pcb *pcb, err_t err)
+{
+  struct netconn *conn;
+  int was_blocking;
+  sys_sem_t* op_completed_sem = NULL;
+
+  LWIP_UNUSED_ARG(pcb);
+
+  conn = (struct netconn *)arg;
+
+  if (conn == NULL) {
+    return ERR_VAL;
+  }
+
+  LWIP_ASSERT("conn->state == NETCONN_CONNECT", conn->state == NETCONN_CONNECT);
+  LWIP_ASSERT("(conn->current_msg != NULL) || conn->in_non_blocking_connect",
+    (conn->current_msg != NULL) || IN_NONBLOCKING_CONNECT(conn));
+
+  if (conn->current_msg != NULL) {
+    conn->current_msg->err = err;
+    op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
+  }
+  if ((NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) && (err == ERR_OK)) {
+    setup_tcp(conn);
+  }
+  was_blocking = !IN_NONBLOCKING_CONNECT(conn);
+  SET_NONBLOCKING_CONNECT(conn, 0);
+  LWIP_ASSERT("blocking connect state error",
+    (was_blocking && op_completed_sem != NULL) ||
+    (!was_blocking && op_completed_sem == NULL));
+  conn->current_msg = NULL;
+  conn->state = NETCONN_NONE;
+  NETCONN_SET_SAFE_ERR(conn, ERR_OK);
+  API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
+
+  if (was_blocking) {
+    sys_sem_signal(op_completed_sem);
+  }
+  return ERR_OK;
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Connect a pcb contained inside a netconn
+ * Called from netconn_connect.
+ *
+ * @param m the api_msg_msg pointing to the connection and containing
+ *          the IP address and port to connect to
+ */
+void
+lwip_netconn_do_connect(void *m)
+{
+  struct api_msg *msg = (struct api_msg*)m;
+
+  if (msg->conn->pcb.tcp == NULL) {
+    /* This may happen when calling netconn_connect() a second time */
+    msg->err = ERR_CLSD;
+  } else {
+    switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+    case NETCONN_RAW:
+      msg->err = raw_connect(msg->conn->pcb.raw, API_EXPR_REF(msg->msg.bc.ipaddr));
+      break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+    case NETCONN_UDP:
+      msg->err = udp_connect(msg->conn->pcb.udp, API_EXPR_REF(msg->msg.bc.ipaddr), msg->msg.bc.port);
+      break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+    case NETCONN_TCP:
+      /* Prevent connect while doing any other action. */
+      if (msg->conn->state == NETCONN_CONNECT) {
+        msg->err = ERR_ALREADY;
+      } else if (msg->conn->state != NETCONN_NONE) {
+        msg->err = ERR_ISCONN;
+      } else {
+        setup_tcp(msg->conn);
+        msg->err = tcp_connect(msg->conn->pcb.tcp, API_EXPR_REF(msg->msg.bc.ipaddr),
+          msg->msg.bc.port, lwip_netconn_do_connected);
+        if (msg->err == ERR_OK) {
+          u8_t non_blocking = netconn_is_nonblocking(msg->conn);
+          msg->conn->state = NETCONN_CONNECT;
+          SET_NONBLOCKING_CONNECT(msg->conn, non_blocking);
+          if (non_blocking) {
+            msg->err = ERR_INPROGRESS;
+          } else {
+            msg->conn->current_msg = msg;
+            /* sys_sem_signal() is called from lwip_netconn_do_connected (or err_tcp()),
+               when the connection is established! */
+#if LWIP_TCPIP_CORE_LOCKING
+            LWIP_ASSERT("state!", msg->conn->state == NETCONN_CONNECT);
+            UNLOCK_TCPIP_CORE();
+            sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0);
+            LOCK_TCPIP_CORE();
+            LWIP_ASSERT("state!", msg->conn->state != NETCONN_CONNECT);
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+            return;
+          }
+        }
+      }
+      break;
+#endif /* LWIP_TCP */
+    default:
+      LWIP_ERROR("Invalid netconn type", 0, do{ msg->err = ERR_VAL; }while(0));
+      break;
+    }
+  }
+  /* For all other protocols, netconn_connect() calls TCPIP_APIMSG(),
+     so use TCPIP_APIMSG_ACK() here. */
+  TCPIP_APIMSG_ACK(msg);
+}
+
+/**
+ * Disconnect a pcb contained inside a netconn
+ * Only used for UDP netconns.
+ * Called from netconn_disconnect.
+ *
+ * @param m the api_msg_msg pointing to the connection to disconnect
+ */
+void
+lwip_netconn_do_disconnect(void *m)
+{
+  struct api_msg *msg = (struct api_msg*)m;
+
+#if LWIP_UDP
+  if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) {
+    udp_disconnect(msg->conn->pcb.udp);
+    msg->err = ERR_OK;
+  } else
+#endif /* LWIP_UDP */
+  {
+    msg->err = ERR_VAL;
+  }
+  TCPIP_APIMSG_ACK(msg);
+}
+
+#if LWIP_TCP
+/**
+ * Set a TCP pcb contained in a netconn into listen mode
+ * Called from netconn_listen.
+ *
+ * @param m the api_msg_msg pointing to the connection
+ */
+void
+lwip_netconn_do_listen(void *m)
+{
+  struct api_msg *msg = (struct api_msg*)m;
+
+  if (ERR_IS_FATAL(msg->conn->last_err)) {
+    msg->err = msg->conn->last_err;
+  } else {
+    msg->err = ERR_CONN;
+    if (msg->conn->pcb.tcp != NULL) {
+      if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) {
+        if (msg->conn->state == NETCONN_NONE) {
+          struct tcp_pcb* lpcb;
+          if (msg->conn->pcb.tcp->state != CLOSED) {
+            /* connection is not closed, cannot listen */
+            msg->err = ERR_VAL;
+          } else {
+            err_t err;
+            u8_t backlog;
+#if TCP_LISTEN_BACKLOG
+            backlog = msg->msg.lb.backlog;
+#else  /* TCP_LISTEN_BACKLOG */
+            backlog = TCP_DEFAULT_LISTEN_BACKLOG;
+#endif /* TCP_LISTEN_BACKLOG */
+#if LWIP_IPV4 && LWIP_IPV6
+            /* "Socket API like" dual-stack support: If IP to listen to is IP6_ADDR_ANY,
+             * and NETCONN_FLAG_IPV6_V6ONLY is NOT set, use IP_ANY_TYPE to listen
+             */
+            if (ip_addr_cmp(&msg->conn->pcb.ip->local_ip, IP6_ADDR_ANY) &&
+                (netconn_get_ipv6only(msg->conn) == 0)) {
+              /* change PCB type to IPADDR_TYPE_ANY */
+              IP_SET_TYPE_VAL(msg->conn->pcb.tcp->local_ip,  IPADDR_TYPE_ANY);
+              IP_SET_TYPE_VAL(msg->conn->pcb.tcp->remote_ip, IPADDR_TYPE_ANY);
+            }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+            lpcb = tcp_listen_with_backlog_and_err(msg->conn->pcb.tcp, backlog, &err);
+
+            if (lpcb == NULL) {
+              /* in this case, the old pcb is still allocated */
+              msg->err = err;
+            } else {
+              /* delete the recvmbox and allocate the acceptmbox */
+              if (sys_mbox_valid(&msg->conn->recvmbox)) {
+                /** @todo: should we drain the recvmbox here? */
+                sys_mbox_free(&msg->conn->recvmbox);
+                sys_mbox_set_invalid(&msg->conn->recvmbox);
+              }
+              msg->err = ERR_OK;
+              if (!sys_mbox_valid(&msg->conn->acceptmbox)) {
+                msg->err = sys_mbox_new(&msg->conn->acceptmbox, DEFAULT_ACCEPTMBOX_SIZE);
+              }
+              if (msg->err == ERR_OK) {
+                msg->conn->state = NETCONN_LISTEN;
+                msg->conn->pcb.tcp = lpcb;
+                tcp_arg(msg->conn->pcb.tcp, msg->conn);
+                tcp_accept(msg->conn->pcb.tcp, accept_function);
+              } else {
+                /* since the old pcb is already deallocated, free lpcb now */
+                tcp_close(lpcb);
+                msg->conn->pcb.tcp = NULL;
+              }
+            }
+          }
+        } else if (msg->conn->state == NETCONN_LISTEN) {
+          /* already listening, allow updating of the backlog */
+          msg->err = ERR_OK;
+          tcp_backlog_set(msg->conn->pcb.tcp, msg->msg.lb.backlog);
+        }
+      } else {
+        msg->err = ERR_ARG;
+      }
+    }
+  }
+  TCPIP_APIMSG_ACK(msg);
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Send some data on a RAW or UDP pcb contained in a netconn
+ * Called from netconn_send
+ *
+ * @param m the api_msg_msg pointing to the connection
+ */
+void
+lwip_netconn_do_send(void *m)
+{
+  struct api_msg *msg = (struct api_msg*)m;
+
+  if (ERR_IS_FATAL(msg->conn->last_err)) {
+    msg->err = msg->conn->last_err;
+  } else {
+    msg->err = ERR_CONN;
+    if (msg->conn->pcb.tcp != NULL) {
+      switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+      case NETCONN_RAW:
+        if (ip_addr_isany(&msg->msg.b->addr) || IP_IS_ANY_TYPE_VAL(msg->msg.b->addr)) {
+          msg->err = raw_send(msg->conn->pcb.raw, msg->msg.b->p);
+        } else {
+          msg->err = raw_sendto(msg->conn->pcb.raw, msg->msg.b->p, &msg->msg.b->addr);
+        }
+        break;
+#endif
+#if LWIP_UDP
+      case NETCONN_UDP:
+#if LWIP_CHECKSUM_ON_COPY
+        if (ip_addr_isany(&msg->msg.b->addr) || IP_IS_ANY_TYPE_VAL(msg->msg.b->addr)) {
+          msg->err = udp_send_chksum(msg->conn->pcb.udp, msg->msg.b->p,
+            msg->msg.b->flags & NETBUF_FLAG_CHKSUM, msg->msg.b->toport_chksum);
+        } else {
+          msg->err = udp_sendto_chksum(msg->conn->pcb.udp, msg->msg.b->p,
+            &msg->msg.b->addr, msg->msg.b->port,
+            msg->msg.b->flags & NETBUF_FLAG_CHKSUM, msg->msg.b->toport_chksum);
+        }
+#else /* LWIP_CHECKSUM_ON_COPY */
+        if (ip_addr_isany_val(msg->msg.b->addr) || IP_IS_ANY_TYPE_VAL(msg->msg.b->addr)) {
+          msg->err = udp_send(msg->conn->pcb.udp, msg->msg.b->p);
+        } else {
+          msg->err = udp_sendto(msg->conn->pcb.udp, msg->msg.b->p, &msg->msg.b->addr, msg->msg.b->port);
+        }
+#endif /* LWIP_CHECKSUM_ON_COPY */
+        break;
+#endif /* LWIP_UDP */
+      default:
+        break;
+      }
+    }
+  }
+  TCPIP_APIMSG_ACK(msg);
+}
+
+#if LWIP_TCP
+/**
+ * Indicate data has been received from a TCP pcb contained in a netconn
+ * Called from netconn_recv
+ *
+ * @param m the api_msg_msg pointing to the connection
+ */
+void
+lwip_netconn_do_recv(void *m)
+{
+  struct api_msg *msg = (struct api_msg*)m;
+
+  msg->err = ERR_OK;
+  if (msg->conn->pcb.tcp != NULL) {
+    if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) {
+      u32_t remaining = msg->msg.r.len;
+      do {
+        u16_t recved = (remaining > 0xffff) ? 0xffff : (u16_t)remaining;
+        tcp_recved(msg->conn->pcb.tcp, recved);
+        remaining -= recved;
+      } while (remaining != 0);
+    }
+  }
+  TCPIP_APIMSG_ACK(msg);
+}
+
+#if TCP_LISTEN_BACKLOG
+/** Indicate that a TCP pcb has been accepted
+ * Called from netconn_accept
+ *
+ * @param m the api_msg_msg pointing to the connection
+ */
+void
+lwip_netconn_do_accepted(void *m)
+{
+  struct api_msg *msg = (struct api_msg*)m;
+
+  msg->err = ERR_OK;
+  if (msg->conn->pcb.tcp != NULL) {
+    if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) {
+      tcp_backlog_accepted(msg->conn->pcb.tcp);
+    }
+  }
+  TCPIP_APIMSG_ACK(msg);
+}
+#endif /* TCP_LISTEN_BACKLOG */
+
+/**
+ * See if more data needs to be written from a previous call to netconn_write.
+ * Called initially from lwip_netconn_do_write. If the first call can't send all data
+ * (because of low memory or empty send-buffer), this function is called again
+ * from sent_tcp() or poll_tcp() to send more data. If all data is sent, the
+ * blocking application thread (waiting in netconn_write) is released.
+ *
+ * @param conn netconn (that is currently in state NETCONN_WRITE) to process
+ * @return ERR_OK
+ *         ERR_MEM if LWIP_TCPIP_CORE_LOCKING=1 and sending hasn't yet finished
+ */
+static err_t
+lwip_netconn_do_writemore(struct netconn *conn  WRITE_DELAYED_PARAM)
+{
+  err_t err;
+  const void *dataptr;
+  u16_t len, available;
+  u8_t write_finished = 0;
+  size_t diff;
+  u8_t dontblock;
+  u8_t apiflags;
+
+  LWIP_ASSERT("conn != NULL", conn != NULL);
+  LWIP_ASSERT("conn->state == NETCONN_WRITE", (conn->state == NETCONN_WRITE));
+  LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
+  LWIP_ASSERT("conn->pcb.tcp != NULL", conn->pcb.tcp != NULL);
+  LWIP_ASSERT("conn->write_offset < conn->current_msg->msg.w.len",
+    conn->write_offset < conn->current_msg->msg.w.len);
+
+  apiflags = conn->current_msg->msg.w.apiflags;
+  dontblock = netconn_is_nonblocking(conn) || (apiflags & NETCONN_DONTBLOCK);
+
+#if LWIP_SO_SNDTIMEO
+  if ((conn->send_timeout != 0) &&
+      ((s32_t)(sys_now() - conn->current_msg->msg.w.time_started) >= conn->send_timeout)) {
+    write_finished = 1;
+    if (conn->write_offset == 0) {
+      /* nothing has been written */
+      err = ERR_WOULDBLOCK;
+      conn->current_msg->msg.w.len = 0;
+    } else {
+      /* partial write */
+      err = ERR_OK;
+      conn->current_msg->msg.w.len = conn->write_offset;
+      conn->write_offset = 0;
+    }
+  } else
+#endif /* LWIP_SO_SNDTIMEO */
+  {
+    dataptr = (const u8_t*)conn->current_msg->msg.w.dataptr + conn->write_offset;
+    diff = conn->current_msg->msg.w.len - conn->write_offset;
+    if (diff > 0xffffUL) { /* max_u16_t */
+      len = 0xffff;
+      apiflags |= TCP_WRITE_FLAG_MORE;
+    } else {
+      len = (u16_t)diff;
+    }
+    available = tcp_sndbuf(conn->pcb.tcp);
+    if (available < len) {
+      /* don't try to write more than sendbuf */
+      len = available;
+      if (dontblock) {
+        if (!len) {
+          err = ERR_WOULDBLOCK;
+          goto err_mem;
+        }
+      } else {
+        apiflags |= TCP_WRITE_FLAG_MORE;
+      }
+    }
+    LWIP_ASSERT("lwip_netconn_do_writemore: invalid length!", ((conn->write_offset + len) <= conn->current_msg->msg.w.len));
+    err = tcp_write(conn->pcb.tcp, dataptr, len, apiflags);
+    /* if OK or memory error, check available space */
+    if ((err == ERR_OK) || (err == ERR_MEM)) {
+err_mem:
+      if (dontblock && (len < conn->current_msg->msg.w.len)) {
+        /* non-blocking write did not write everything: mark the pcb non-writable
+           and let poll_tcp check writable space to mark the pcb writable again */
+        API_EVENT(conn, NETCONN_EVT_SENDMINUS, len);
+        conn->flags |= NETCONN_FLAG_CHECK_WRITESPACE;
+      } else if ((tcp_sndbuf(conn->pcb.tcp) <= TCP_SNDLOWAT) ||
+                 (tcp_sndqueuelen(conn->pcb.tcp) >= TCP_SNDQUEUELOWAT)) {
+        /* The queued byte- or pbuf-count exceeds the configured low-water limit,
+           let select mark this pcb as non-writable. */
+        API_EVENT(conn, NETCONN_EVT_SENDMINUS, len);
+      }
+    }
+
+    if (err == ERR_OK) {
+      err_t out_err;
+      conn->write_offset += len;
+      if ((conn->write_offset == conn->current_msg->msg.w.len) || dontblock) {
+        /* return sent length */
+        conn->current_msg->msg.w.len = conn->write_offset;
+        /* everything was written */
+        write_finished = 1;
+      }
+      out_err = tcp_output(conn->pcb.tcp);
+      if (ERR_IS_FATAL(out_err) || (out_err == ERR_RTE)) {
+        /* If tcp_output fails with fatal error or no route is found,
+           don't try writing any more but return the error
+           to the application thread. */
+        err = out_err;
+        write_finished = 1;
+        conn->current_msg->msg.w.len = 0;
+      }
+    } else if (err == ERR_MEM) {
+      /* If ERR_MEM, we wait for sent_tcp or poll_tcp to be called.
+         For blocking sockets, we do NOT return to the application
+         thread, since ERR_MEM is only a temporary error! Non-blocking
+         will remain non-writable until sent_tcp/poll_tcp is called */
+
+      /* tcp_write returned ERR_MEM, try tcp_output anyway */
+      err_t out_err = tcp_output(conn->pcb.tcp);
+      if (ERR_IS_FATAL(out_err) || (out_err == ERR_RTE)) {
+        /* If tcp_output fails with fatal error or no route is found,
+           don't try writing any more but return the error
+           to the application thread. */
+        err = out_err;
+        write_finished = 1;
+        conn->current_msg->msg.w.len = 0;
+      } else if (dontblock) {
+        /* non-blocking write is done on ERR_MEM */
+        err = ERR_WOULDBLOCK;
+        write_finished = 1;
+        conn->current_msg->msg.w.len = 0;
+      }
+    } else {
+      /* On errors != ERR_MEM, we don't try writing any more but return
+         the error to the application thread. */
+      write_finished = 1;
+      conn->current_msg->msg.w.len = 0;
+    }
+  }
+  if (write_finished) {
+    /* everything was written: set back connection state
+       and back to application task */
+    sys_sem_t* op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
+    conn->current_msg->err = err;
+    conn->current_msg = NULL;
+    conn->write_offset = 0;
+    conn->state = NETCONN_NONE;
+    NETCONN_SET_SAFE_ERR(conn, err);
+#if LWIP_TCPIP_CORE_LOCKING
+    if (delayed)
+#endif
+    {
+      sys_sem_signal(op_completed_sem);
+    }
+  }
+#if LWIP_TCPIP_CORE_LOCKING
+  else {
+    return ERR_MEM;
+  }
+#endif
+  return ERR_OK;
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Send some data on a TCP pcb contained in a netconn
+ * Called from netconn_write
+ *
+ * @param m the api_msg_msg pointing to the connection
+ */
+void
+lwip_netconn_do_write(void *m)
+{
+  struct api_msg *msg = (struct api_msg*)m;
+
+  if (ERR_IS_FATAL(msg->conn->last_err)) {
+    msg->err = msg->conn->last_err;
+  } else {
+    if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) {
+#if LWIP_TCP
+      if (msg->conn->state != NETCONN_NONE) {
+        /* netconn is connecting, closing or in blocking write */
+        msg->err = ERR_INPROGRESS;
+      } else if (msg->conn->pcb.tcp != NULL) {
+        msg->conn->state = NETCONN_WRITE;
+        /* set all the variables used by lwip_netconn_do_writemore */
+        LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL &&
+          msg->conn->write_offset == 0);
+        LWIP_ASSERT("msg->msg.w.len != 0", msg->msg.w.len != 0);
+        msg->conn->current_msg = msg;
+        msg->conn->write_offset = 0;
+#if LWIP_TCPIP_CORE_LOCKING
+        if (lwip_netconn_do_writemore(msg->conn, 0) != ERR_OK) {
+          LWIP_ASSERT("state!", msg->conn->state == NETCONN_WRITE);
+          UNLOCK_TCPIP_CORE();
+          sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0);
+          LOCK_TCPIP_CORE();
+          LWIP_ASSERT("state!", msg->conn->state != NETCONN_WRITE);
+        }
+#else /* LWIP_TCPIP_CORE_LOCKING */
+        lwip_netconn_do_writemore(msg->conn);
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+        /* for both cases: if lwip_netconn_do_writemore was called, don't ACK the APIMSG
+           since lwip_netconn_do_writemore ACKs it! */
+        return;
+      } else {
+        msg->err = ERR_CONN;
+      }
+#else /* LWIP_TCP */
+      msg->err = ERR_VAL;
+#endif /* LWIP_TCP */
+#if (LWIP_UDP || LWIP_RAW)
+    } else {
+      msg->err = ERR_VAL;
+#endif /* (LWIP_UDP || LWIP_RAW) */
+    }
+  }
+  TCPIP_APIMSG_ACK(msg);
+}
+
+/**
+ * Return a connection's local or remote address
+ * Called from netconn_getaddr
+ *
+ * @param m the api_msg_msg pointing to the connection
+ */
+void
+lwip_netconn_do_getaddr(void *m)
+{
+  struct api_msg *msg = (struct api_msg*)m;
+
+  if (msg->conn->pcb.ip != NULL) {
+    if (msg->msg.ad.local) {
+      ip_addr_copy(API_EXPR_DEREF(msg->msg.ad.ipaddr),
+        msg->conn->pcb.ip->local_ip);
+    } else {
+      ip_addr_copy(API_EXPR_DEREF(msg->msg.ad.ipaddr),
+        msg->conn->pcb.ip->remote_ip);
+    }
+
+    msg->err = ERR_OK;
+    switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+    case NETCONN_RAW:
+      if (msg->msg.ad.local) {
+        API_EXPR_DEREF(msg->msg.ad.port) = msg->conn->pcb.raw->protocol;
+      } else {
+        /* return an error as connecting is only a helper for upper layers */
+        msg->err = ERR_CONN;
+      }
+      break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+    case NETCONN_UDP:
+      if (msg->msg.ad.local) {
+        API_EXPR_DEREF(msg->msg.ad.port) = msg->conn->pcb.udp->local_port;
+      } else {
+        if ((msg->conn->pcb.udp->flags & UDP_FLAGS_CONNECTED) == 0) {
+          msg->err = ERR_CONN;
+        } else {
+          API_EXPR_DEREF(msg->msg.ad.port) = msg->conn->pcb.udp->remote_port;
+        }
+      }
+      break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+    case NETCONN_TCP:
+      if ((msg->msg.ad.local == 0) &&
+          ((msg->conn->pcb.tcp->state == CLOSED) || (msg->conn->pcb.tcp->state == LISTEN))) {
+        /* pcb is not connected and remote name is requested */
+        msg->err = ERR_CONN;
+      } else {
+        API_EXPR_DEREF(msg->msg.ad.port) = (msg->msg.ad.local ? msg->conn->pcb.tcp->local_port : msg->conn->pcb.tcp->remote_port);
+      }
+      break;
+#endif /* LWIP_TCP */
+    default:
+      LWIP_ASSERT("invalid netconn_type", 0);
+      break;
+    }
+  } else {
+    msg->err = ERR_CONN;
+  }
+  TCPIP_APIMSG_ACK(msg);
+}
+
+/**
+ * Close or half-shutdown a TCP pcb contained in a netconn
+ * Called from netconn_close
+ * In contrast to closing sockets, the netconn is not deallocated.
+ *
+ * @param m the api_msg_msg pointing to the connection
+ */
+void
+lwip_netconn_do_close(void *m)
+{
+  struct api_msg *msg = (struct api_msg*)m;
+
+#if LWIP_TCP
+  enum netconn_state state = msg->conn->state;
+  /* First check if this is a TCP netconn and if it is in a correct state
+      (LISTEN doesn't support half shutdown) */
+  if ((msg->conn->pcb.tcp != NULL) &&
+      (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) &&
+      ((msg->msg.sd.shut == NETCONN_SHUT_RDWR) || (state != NETCONN_LISTEN))) {
+    /* Check if we are in a connected state */
+    if (state == NETCONN_CONNECT) {
+      /* TCP connect in progress: cannot shutdown */
+      msg->err = ERR_CONN;
+    } else if (state == NETCONN_WRITE) {
+#if LWIP_NETCONN_FULLDUPLEX
+      if (msg->msg.sd.shut & NETCONN_SHUT_WR) {
+        /* close requested, abort running write */
+        sys_sem_t* write_completed_sem;
+        LWIP_ASSERT("msg->conn->current_msg != NULL", msg->conn->current_msg != NULL);
+        write_completed_sem = LWIP_API_MSG_SEM(msg->conn->current_msg);
+        msg->conn->current_msg->err = ERR_CLSD;
+        msg->conn->current_msg = NULL;
+        msg->conn->write_offset = 0;
+        msg->conn->state = NETCONN_NONE;
+        state = NETCONN_NONE;
+        NETCONN_SET_SAFE_ERR(msg->conn, ERR_CLSD);
+        sys_sem_signal(write_completed_sem);
+      } else {
+        LWIP_ASSERT("msg->msg.sd.shut == NETCONN_SHUT_RD", msg->msg.sd.shut == NETCONN_SHUT_RD);
+        /* In this case, let the write continue and do not interfere with
+           conn->current_msg or conn->state! */
+        msg->err = tcp_shutdown(msg->conn->pcb.tcp, 1, 0);
+      }
+    }
+    if (state == NETCONN_NONE) {
+#else /* LWIP_NETCONN_FULLDUPLEX */
+      msg->err = ERR_INPROGRESS;
+    } else {
+#endif /* LWIP_NETCONN_FULLDUPLEX */
+      if (msg->msg.sd.shut & NETCONN_SHUT_RD) {
+        /* Drain and delete mboxes */
+        netconn_drain(msg->conn);
+      }
+      LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL &&
+        msg->conn->write_offset == 0);
+      msg->conn->state = NETCONN_CLOSE;
+      msg->conn->current_msg = msg;
+#if LWIP_TCPIP_CORE_LOCKING
+      if (lwip_netconn_do_close_internal(msg->conn, 0) != ERR_OK) {
+        LWIP_ASSERT("state!", msg->conn->state == NETCONN_CLOSE);
+        UNLOCK_TCPIP_CORE();
+        sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0);
+        LOCK_TCPIP_CORE();
+        LWIP_ASSERT("state!", msg->conn->state == NETCONN_NONE);
+      }
+#else /* LWIP_TCPIP_CORE_LOCKING */
+      lwip_netconn_do_close_internal(msg->conn);
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+      /* for tcp netconns, lwip_netconn_do_close_internal ACKs the message */
+      return;
+    }
+  } else
+#endif /* LWIP_TCP */
+  {
+    msg->err = ERR_CONN;
+  }
+  TCPIP_APIMSG_ACK(msg);
+}
+
+#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD)
+/**
+ * Join multicast groups for UDP netconns.
+ * Called from netconn_join_leave_group
+ *
+ * @param m the api_msg_msg pointing to the connection
+ */
+void
+lwip_netconn_do_join_leave_group(void *m)
+{
+  struct api_msg *msg = (struct api_msg*)m;
+
+  if (ERR_IS_FATAL(msg->conn->last_err)) {
+    msg->err = msg->conn->last_err;
+  } else {
+    if (msg->conn->pcb.tcp != NULL) {
+      if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) {
+#if LWIP_UDP
+#if LWIP_IPV6 && LWIP_IPV6_MLD
+        if (NETCONNTYPE_ISIPV6(msg->conn->type)) {
+          if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
+            msg->err = mld6_joingroup(ip_2_ip6(API_EXPR_REF(msg->msg.jl.netif_addr)),
+              ip_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr)));
+          } else {
+            msg->err = mld6_leavegroup(ip_2_ip6(API_EXPR_REF(msg->msg.jl.netif_addr)),
+              ip_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr)));
+          }
+        }
+        else
+#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
+        {
+#if LWIP_IGMP
+          if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
+            msg->err = igmp_joingroup(ip_2_ip4(API_EXPR_REF(msg->msg.jl.netif_addr)),
+              ip_2_ip4(API_EXPR_REF(msg->msg.jl.multiaddr)));
+          } else {
+            msg->err = igmp_leavegroup(ip_2_ip4(API_EXPR_REF(msg->msg.jl.netif_addr)),
+              ip_2_ip4(API_EXPR_REF(msg->msg.jl.multiaddr)));
+          }
+#endif /* LWIP_IGMP */
+        }
+#endif /* LWIP_UDP */
+#if (LWIP_TCP || LWIP_RAW)
+      } else {
+        msg->err = ERR_VAL;
+#endif /* (LWIP_TCP || LWIP_RAW) */
+      }
+    } else {
+      msg->err = ERR_CONN;
+    }
+  }
+  TCPIP_APIMSG_ACK(msg);
+}
+#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */
+
+#if LWIP_DNS
+/**
+ * Callback function that is called when DNS name is resolved
+ * (or on timeout). A waiting application thread is waked up by
+ * signaling the semaphore.
+ */
+static void
+lwip_netconn_do_dns_found(const char *name, const ip_addr_t *ipaddr, void *arg)
+{
+  struct dns_api_msg *msg = (struct dns_api_msg*)arg;
+
+  /* we trust the internal implementation to be correct :-) */
+  LWIP_UNUSED_ARG(name);
+
+  if (ipaddr == NULL) {
+    /* timeout or memory error */
+    API_EXPR_DEREF(msg->err) = ERR_VAL;
+  } else {
+    /* address was resolved */
+    API_EXPR_DEREF(msg->err) = ERR_OK;
+    API_EXPR_DEREF(msg->addr) = *ipaddr;
+  }
+  /* wake up the application task waiting in netconn_gethostbyname */
+  sys_sem_signal(API_EXPR_REF_SEM(msg->sem));
+}
+
+/**
+ * Execute a DNS query
+ * Called from netconn_gethostbyname
+ *
+ * @param arg the dns_api_msg pointing to the query
+ */
+void
+lwip_netconn_do_gethostbyname(void *arg)
+{
+  struct dns_api_msg *msg = (struct dns_api_msg*)arg;
+  u8_t addrtype =
+#if LWIP_IPV4 && LWIP_IPV6
+    msg->dns_addrtype;
+#else
+    LWIP_DNS_ADDRTYPE_DEFAULT;
+#endif
+
+  API_EXPR_DEREF(msg->err) = dns_gethostbyname_addrtype(msg->name,
+    API_EXPR_REF(msg->addr), lwip_netconn_do_dns_found, msg, addrtype);
+  if (API_EXPR_DEREF(msg->err) != ERR_INPROGRESS) {
+    /* on error or immediate success, wake up the application
+     * task waiting in netconn_gethostbyname */
+    sys_sem_signal(API_EXPR_REF_SEM(msg->sem));
+  }
+}
+#endif /* LWIP_DNS */
+
+#endif /* LWIP_NETCONN */

+ 115 - 115
libs/thirdparty/lwip_2.1.2/src/api/err.c → libs/thirdparty/LwIP/src/api/err.c

@@ -1,115 +1,115 @@
-/**
- * @file
- * Error Management module
- *
- */
-
-/*
- * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- *
- * Author: Adam Dunkels <adam@sics.se>
- *
- */
-
-#include "lwip/err.h"
-#include "lwip/def.h"
-#include "lwip/sys.h"
-
-#include "lwip/errno.h"
-
-#if !NO_SYS
-/** Table to quickly map an lwIP error (err_t) to a socket error
-  * by using -err as an index */
-static const int err_to_errno_table[] = {
-  0,             /* ERR_OK          0      No error, everything OK. */
-  ENOMEM,        /* ERR_MEM        -1      Out of memory error.     */
-  ENOBUFS,       /* ERR_BUF        -2      Buffer error.            */
-  EWOULDBLOCK,   /* ERR_TIMEOUT    -3      Timeout                  */
-  EHOSTUNREACH,  /* ERR_RTE        -4      Routing problem.         */
-  EINPROGRESS,   /* ERR_INPROGRESS -5      Operation in progress    */
-  EINVAL,        /* ERR_VAL        -6      Illegal value.           */
-  EWOULDBLOCK,   /* ERR_WOULDBLOCK -7      Operation would block.   */
-  EADDRINUSE,    /* ERR_USE        -8      Address in use.          */
-  EALREADY,      /* ERR_ALREADY    -9      Already connecting.      */
-  EISCONN,       /* ERR_ISCONN     -10     Conn already established.*/
-  ENOTCONN,      /* ERR_CONN       -11     Not connected.           */
-  -1,            /* ERR_IF         -12     Low-level netif error    */
-  ECONNABORTED,  /* ERR_ABRT       -13     Connection aborted.      */
-  ECONNRESET,    /* ERR_RST        -14     Connection reset.        */
-  ENOTCONN,      /* ERR_CLSD       -15     Connection closed.       */
-  EIO            /* ERR_ARG        -16     Illegal argument.        */
-};
-
-int
-err_to_errno(err_t err)
-{
-  if ((err > 0) || (-err >= (err_t)LWIP_ARRAYSIZE(err_to_errno_table))) {
-    return EIO;
-  }
-  return err_to_errno_table[-err];
-}
-#endif /* !NO_SYS */
-
-#ifdef LWIP_DEBUG
-
-static const char *err_strerr[] = {
-  "Ok.",                    /* ERR_OK          0  */
-  "Out of memory error.",   /* ERR_MEM        -1  */
-  "Buffer error.",          /* ERR_BUF        -2  */
-  "Timeout.",               /* ERR_TIMEOUT    -3  */
-  "Routing problem.",       /* ERR_RTE        -4  */
-  "Operation in progress.", /* ERR_INPROGRESS -5  */
-  "Illegal value.",         /* ERR_VAL        -6  */
-  "Operation would block.", /* ERR_WOULDBLOCK -7  */
-  "Address in use.",        /* ERR_USE        -8  */
-  "Already connecting.",    /* ERR_ALREADY    -9  */
-  "Already connected.",     /* ERR_ISCONN     -10 */
-  "Not connected.",         /* ERR_CONN       -11 */
-  "Low-level netif error.", /* ERR_IF         -12 */
-  "Connection aborted.",    /* ERR_ABRT       -13 */
-  "Connection reset.",      /* ERR_RST        -14 */
-  "Connection closed.",     /* ERR_CLSD       -15 */
-  "Illegal argument."       /* ERR_ARG        -16 */
-};
-
-/**
- * Convert an lwip internal error to a string representation.
- *
- * @param err an lwip internal err_t
- * @return a string representation for err
- */
-const char *
-lwip_strerr(err_t err)
-{
-  if ((err > 0) || (-err >= (err_t)LWIP_ARRAYSIZE(err_strerr))) {
-    return "Unknown error.";
-  }
-  return err_strerr[-err];
-}
-
-#endif /* LWIP_DEBUG */
+/**
+ * @file
+ * Error Management module
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/err.h"
+#include "lwip/def.h"
+#include "lwip/sys.h"
+
+#include "lwip/errno.h"
+
+#if !NO_SYS
+/** Table to quickly map an lwIP error (err_t) to a socket error
+  * by using -err as an index */
+static const int err_to_errno_table[] = {
+  0,             /* ERR_OK          0      No error, everything OK. */
+  ENOMEM,        /* ERR_MEM        -1      Out of memory error.     */
+  ENOBUFS,       /* ERR_BUF        -2      Buffer error.            */
+  EWOULDBLOCK,   /* ERR_TIMEOUT    -3      Timeout                  */
+  EHOSTUNREACH,  /* ERR_RTE        -4      Routing problem.         */
+  EINPROGRESS,   /* ERR_INPROGRESS -5      Operation in progress    */
+  EINVAL,        /* ERR_VAL        -6      Illegal value.           */
+  EWOULDBLOCK,   /* ERR_WOULDBLOCK -7      Operation would block.   */
+  EADDRINUSE,    /* ERR_USE        -8      Address in use.          */
+  EALREADY,      /* ERR_ALREADY    -9      Already connecting.      */
+  EISCONN,       /* ERR_ISCONN     -10     Conn already established.*/
+  ENOTCONN,      /* ERR_CONN       -11     Not connected.           */
+  -1,            /* ERR_IF         -12     Low-level netif error    */
+  ECONNABORTED,  /* ERR_ABRT       -13     Connection aborted.      */
+  ECONNRESET,    /* ERR_RST        -14     Connection reset.        */
+  ENOTCONN,      /* ERR_CLSD       -15     Connection closed.       */
+  EIO            /* ERR_ARG        -16     Illegal argument.        */
+};
+
+int
+err_to_errno(err_t err)
+{
+  if ((err > 0) || (-err >= (err_t)LWIP_ARRAYSIZE(err_to_errno_table))) {
+    return EIO;
+  }
+  return err_to_errno_table[-err];
+}
+#endif /* !NO_SYS */
+
+#ifdef LWIP_DEBUG
+
+static const char *err_strerr[] = {
+           "Ok.",                    /* ERR_OK          0  */
+           "Out of memory error.",   /* ERR_MEM        -1  */
+           "Buffer error.",          /* ERR_BUF        -2  */
+           "Timeout.",               /* ERR_TIMEOUT    -3  */
+           "Routing problem.",       /* ERR_RTE        -4  */
+           "Operation in progress.", /* ERR_INPROGRESS -5  */
+           "Illegal value.",         /* ERR_VAL        -6  */
+           "Operation would block.", /* ERR_WOULDBLOCK -7  */
+           "Address in use.",        /* ERR_USE        -8  */
+           "Already connecting.",    /* ERR_ALREADY    -9  */
+           "Already connected.",     /* ERR_ISCONN     -10 */
+           "Not connected.",         /* ERR_CONN       -11 */
+           "Low-level netif error.", /* ERR_IF         -12 */
+           "Connection aborted.",    /* ERR_ABRT       -13 */
+           "Connection reset.",      /* ERR_RST        -14 */
+           "Connection closed.",     /* ERR_CLSD       -15 */
+           "Illegal argument."       /* ERR_ARG        -16 */
+};
+
+/**
+ * Convert an lwip internal error to a string representation.
+ *
+ * @param err an lwip internal err_t
+ * @return a string representation for err
+ */
+const char *
+lwip_strerr(err_t err)
+{
+  if ((err > 0) || (-err >= (err_t)LWIP_ARRAYSIZE(err_strerr))) {
+    return "Unknown error.";
+  }
+  return err_strerr[-err];
+}
+
+#endif /* LWIP_DEBUG */

+ 246 - 250
libs/thirdparty/lwip_2.1.2/src/api/netbuf.c → libs/thirdparty/LwIP/src/api/netbuf.c

@@ -1,250 +1,246 @@
-/**
- * @file
- * Network buffer management
- *
- * @defgroup netbuf Network buffers
- * @ingroup netconn
- * Network buffer descriptor for @ref netconn. Based on @ref pbuf internally
- * to avoid copying data around.\n
- * Buffers must not be shared accross multiple threads, all functions except
- * netbuf_new() and netbuf_delete() are not thread-safe.
- */
-
-/*
- * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- *
- * Author: Adam Dunkels <adam@sics.se>
- *
- */
-
-#include "lwip/opt.h"
-
-#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
-
-#include "lwip/netbuf.h"
-#include "lwip/memp.h"
-
-#include <string.h>
-
-/**
- * @ingroup netbuf
- * Create (allocate) and initialize a new netbuf.
- * The netbuf doesn't yet contain a packet buffer!
- *
- * @return a pointer to a new netbuf
- *         NULL on lack of memory
- */
-struct
-netbuf *netbuf_new(void)
-{
-  struct netbuf *buf;
-
-  buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
-  if (buf != NULL) {
-    memset(buf, 0, sizeof(struct netbuf));
-  }
-  return buf;
-}
-
-/**
- * @ingroup netbuf
- * Deallocate a netbuf allocated by netbuf_new().
- *
- * @param buf pointer to a netbuf allocated by netbuf_new()
- */
-void
-netbuf_delete(struct netbuf *buf)
-{
-  if (buf != NULL) {
-    if (buf->p != NULL) {
-      pbuf_free(buf->p);
-      buf->p = buf->ptr = NULL;
-    }
-    memp_free(MEMP_NETBUF, buf);
-  }
-}
-
-/**
- * @ingroup netbuf
- * Allocate memory for a packet buffer for a given netbuf.
- *
- * @param buf the netbuf for which to allocate a packet buffer
- * @param size the size of the packet buffer to allocate
- * @return pointer to the allocated memory
- *         NULL if no memory could be allocated
- */
-void *
-netbuf_alloc(struct netbuf *buf, u16_t size)
-{
-  LWIP_ERROR("netbuf_alloc: invalid buf", (buf != NULL), return NULL;);
-
-  /* Deallocate any previously allocated memory. */
-  if (buf->p != NULL) {
-    pbuf_free(buf->p);
-  }
-  buf->p = pbuf_alloc(PBUF_TRANSPORT, size, PBUF_RAM);
-  if (buf->p == NULL) {
-    return NULL;
-  }
-  LWIP_ASSERT("check that first pbuf can hold size",
-              (buf->p->len >= size));
-  buf->ptr = buf->p;
-  return buf->p->payload;
-}
-
-/**
- * @ingroup netbuf
- * Free the packet buffer included in a netbuf
- *
- * @param buf pointer to the netbuf which contains the packet buffer to free
- */
-void
-netbuf_free(struct netbuf *buf)
-{
-  LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return;);
-  if (buf->p != NULL) {
-    pbuf_free(buf->p);
-  }
-  buf->p = buf->ptr = NULL;
-#if LWIP_CHECKSUM_ON_COPY
-  buf->flags = 0;
-  buf->toport_chksum = 0;
-#endif /* LWIP_CHECKSUM_ON_COPY */
-}
-
-/**
- * @ingroup netbuf
- * Let a netbuf reference existing (non-volatile) data.
- *
- * @param buf netbuf which should reference the data
- * @param dataptr pointer to the data to reference
- * @param size size of the data
- * @return ERR_OK if data is referenced
- *         ERR_MEM if data couldn't be referenced due to lack of memory
- */
-err_t
-netbuf_ref(struct netbuf *buf, const void *dataptr, u16_t size)
-{
-  LWIP_ERROR("netbuf_ref: invalid buf", (buf != NULL), return ERR_ARG;);
-  if (buf->p != NULL) {
-    pbuf_free(buf->p);
-  }
-  buf->p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_REF);
-  if (buf->p == NULL) {
-    buf->ptr = NULL;
-    return ERR_MEM;
-  }
-  ((struct pbuf_rom *)buf->p)->payload = dataptr;
-  buf->p->len = buf->p->tot_len = size;
-  buf->ptr = buf->p;
-  return ERR_OK;
-}
-
-/**
- * @ingroup netbuf
- * Chain one netbuf to another (@see pbuf_chain)
- *
- * @param head the first netbuf
- * @param tail netbuf to chain after head, freed by this function, may not be reference after returning
- */
-void
-netbuf_chain(struct netbuf *head, struct netbuf *tail)
-{
-  LWIP_ERROR("netbuf_chain: invalid head", (head != NULL), return;);
-  LWIP_ERROR("netbuf_chain: invalid tail", (tail != NULL), return;);
-  pbuf_cat(head->p, tail->p);
-  head->ptr = head->p;
-  memp_free(MEMP_NETBUF, tail);
-}
-
-/**
- * @ingroup netbuf
- * Get the data pointer and length of the data inside a netbuf.
- *
- * @param buf netbuf to get the data from
- * @param dataptr pointer to a void pointer where to store the data pointer
- * @param len pointer to an u16_t where the length of the data is stored
- * @return ERR_OK if the information was retrieved,
- *         ERR_BUF on error.
- */
-err_t
-netbuf_data(struct netbuf *buf, void **dataptr, u16_t *len)
-{
-  LWIP_ERROR("netbuf_data: invalid buf", (buf != NULL), return ERR_ARG;);
-  LWIP_ERROR("netbuf_data: invalid dataptr", (dataptr != NULL), return ERR_ARG;);
-  LWIP_ERROR("netbuf_data: invalid len", (len != NULL), return ERR_ARG;);
-
-  if (buf->ptr == NULL) {
-    return ERR_BUF;
-  }
-  *dataptr = buf->ptr->payload;
-  *len = buf->ptr->len;
-  return ERR_OK;
-}
-
-/**
- * @ingroup netbuf
- * Move the current data pointer of a packet buffer contained in a netbuf
- * to the next part.
- * The packet buffer itself is not modified.
- *
- * @param buf the netbuf to modify
- * @return -1 if there is no next part
- *         1  if moved to the next part but now there is no next part
- *         0  if moved to the next part and there are still more parts
- */
-s8_t
-netbuf_next(struct netbuf *buf)
-{
-  LWIP_ERROR("netbuf_next: invalid buf", (buf != NULL), return -1;);
-  if (buf->ptr->next == NULL) {
-    return -1;
-  }
-  buf->ptr = buf->ptr->next;
-  if (buf->ptr->next == NULL) {
-    return 1;
-  }
-  return 0;
-}
-
-/**
- * @ingroup netbuf
- * Move the current data pointer of a packet buffer contained in a netbuf
- * to the beginning of the packet.
- * The packet buffer itself is not modified.
- *
- * @param buf the netbuf to modify
- */
-void
-netbuf_first(struct netbuf *buf)
-{
-  LWIP_ERROR("netbuf_first: invalid buf", (buf != NULL), return;);
-  buf->ptr = buf->p;
-}
-
-#endif /* LWIP_NETCONN */
+/**
+ * @file
+ * Network buffer management
+ *
+ * @defgroup netbuf Network buffers
+ * @ingroup netconn
+ * Network buffer descriptor for @ref netconn. Based on @ref pbuf internally
+ * to avoid copying data around.\n
+ * Buffers must not be shared accross multiple threads, all functions except
+ * netbuf_new() and netbuf_delete() are not thread-safe.
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/netbuf.h"
+#include "lwip/memp.h"
+
+#include <string.h>
+
+/**
+ * @ingroup netbuf
+ * Create (allocate) and initialize a new netbuf.
+ * The netbuf doesn't yet contain a packet buffer!
+ *
+ * @return a pointer to a new netbuf
+ *         NULL on lack of memory
+ */
+struct
+netbuf *netbuf_new(void)
+{
+  struct netbuf *buf;
+
+  buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
+  if (buf != NULL) {
+    memset(buf, 0, sizeof(struct netbuf));
+  }
+  return buf;
+}
+
+/**
+ * @ingroup netbuf
+ * Deallocate a netbuf allocated by netbuf_new().
+ *
+ * @param buf pointer to a netbuf allocated by netbuf_new()
+ */
+void
+netbuf_delete(struct netbuf *buf)
+{
+  if (buf != NULL) {
+    if (buf->p != NULL) {
+      pbuf_free(buf->p);
+      buf->p = buf->ptr = NULL;
+    }
+    memp_free(MEMP_NETBUF, buf);
+  }
+}
+
+/**
+ * @ingroup netbuf
+ * Allocate memory for a packet buffer for a given netbuf.
+ *
+ * @param buf the netbuf for which to allocate a packet buffer
+ * @param size the size of the packet buffer to allocate
+ * @return pointer to the allocated memory
+ *         NULL if no memory could be allocated
+ */
+void *
+netbuf_alloc(struct netbuf *buf, u16_t size)
+{
+  LWIP_ERROR("netbuf_alloc: invalid buf", (buf != NULL), return NULL;);
+
+  /* Deallocate any previously allocated memory. */
+  if (buf->p != NULL) {
+    pbuf_free(buf->p);
+  }
+  buf->p = pbuf_alloc(PBUF_TRANSPORT, size, PBUF_RAM);
+  if (buf->p == NULL) {
+     return NULL;
+  }
+  LWIP_ASSERT("check that first pbuf can hold size",
+             (buf->p->len >= size));
+  buf->ptr = buf->p;
+  return buf->p->payload;
+}
+
+/**
+ * @ingroup netbuf
+ * Free the packet buffer included in a netbuf
+ *
+ * @param buf pointer to the netbuf which contains the packet buffer to free
+ */
+void
+netbuf_free(struct netbuf *buf)
+{
+  LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return;);
+  if (buf->p != NULL) {
+    pbuf_free(buf->p);
+  }
+  buf->p = buf->ptr = NULL;
+}
+
+/**
+ * @ingroup netbuf
+ * Let a netbuf reference existing (non-volatile) data.
+ *
+ * @param buf netbuf which should reference the data
+ * @param dataptr pointer to the data to reference
+ * @param size size of the data
+ * @return ERR_OK if data is referenced
+ *         ERR_MEM if data couldn't be referenced due to lack of memory
+ */
+err_t
+netbuf_ref(struct netbuf *buf, const void *dataptr, u16_t size)
+{
+  LWIP_ERROR("netbuf_ref: invalid buf", (buf != NULL), return ERR_ARG;);
+  if (buf->p != NULL) {
+    pbuf_free(buf->p);
+  }
+  buf->p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_REF);
+  if (buf->p == NULL) {
+    buf->ptr = NULL;
+    return ERR_MEM;
+  }
+  ((struct pbuf_rom*)buf->p)->payload = dataptr;
+  buf->p->len = buf->p->tot_len = size;
+  buf->ptr = buf->p;
+  return ERR_OK;
+}
+
+/**
+ * @ingroup netbuf
+ * Chain one netbuf to another (@see pbuf_chain)
+ *
+ * @param head the first netbuf
+ * @param tail netbuf to chain after head, freed by this function, may not be reference after returning
+ */
+void
+netbuf_chain(struct netbuf *head, struct netbuf *tail)
+{
+  LWIP_ERROR("netbuf_chain: invalid head", (head != NULL), return;);
+  LWIP_ERROR("netbuf_chain: invalid tail", (tail != NULL), return;);
+  pbuf_cat(head->p, tail->p);
+  head->ptr = head->p;
+  memp_free(MEMP_NETBUF, tail);
+}
+
+/**
+ * @ingroup netbuf
+ * Get the data pointer and length of the data inside a netbuf.
+ *
+ * @param buf netbuf to get the data from
+ * @param dataptr pointer to a void pointer where to store the data pointer
+ * @param len pointer to an u16_t where the length of the data is stored
+ * @return ERR_OK if the information was retrieved,
+ *         ERR_BUF on error.
+ */
+err_t
+netbuf_data(struct netbuf *buf, void **dataptr, u16_t *len)
+{
+  LWIP_ERROR("netbuf_data: invalid buf", (buf != NULL), return ERR_ARG;);
+  LWIP_ERROR("netbuf_data: invalid dataptr", (dataptr != NULL), return ERR_ARG;);
+  LWIP_ERROR("netbuf_data: invalid len", (len != NULL), return ERR_ARG;);
+
+  if (buf->ptr == NULL) {
+    return ERR_BUF;
+  }
+  *dataptr = buf->ptr->payload;
+  *len = buf->ptr->len;
+  return ERR_OK;
+}
+
+/**
+ * @ingroup netbuf
+ * Move the current data pointer of a packet buffer contained in a netbuf
+ * to the next part.
+ * The packet buffer itself is not modified.
+ *
+ * @param buf the netbuf to modify
+ * @return -1 if there is no next part
+ *         1  if moved to the next part but now there is no next part
+ *         0  if moved to the next part and there are still more parts
+ */
+s8_t
+netbuf_next(struct netbuf *buf)
+{
+  LWIP_ERROR("netbuf_next: invalid buf", (buf != NULL), return -1;);
+  if (buf->ptr->next == NULL) {
+    return -1;
+  }
+  buf->ptr = buf->ptr->next;
+  if (buf->ptr->next == NULL) {
+    return 1;
+  }
+  return 0;
+}
+
+/**
+ * @ingroup netbuf
+ * Move the current data pointer of a packet buffer contained in a netbuf
+ * to the beginning of the packet.
+ * The packet buffer itself is not modified.
+ *
+ * @param buf the netbuf to modify
+ */
+void
+netbuf_first(struct netbuf *buf)
+{
+  LWIP_ERROR("netbuf_first: invalid buf", (buf != NULL), return;);
+  buf->ptr = buf->p;
+}
+
+#endif /* LWIP_NETCONN */

+ 413 - 414
libs/thirdparty/lwip_2.1.2/src/api/netdb.c → libs/thirdparty/LwIP/src/api/netdb.c

@@ -1,414 +1,413 @@
-/**
- * @file
- * API functions for name resolving
- *
- * @defgroup netdbapi NETDB API
- * @ingroup socket
- */
-
-/*
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- *
- * Author: Simon Goldschmidt
- *
- */
-
-#include "lwip/netdb.h"
-
-#if LWIP_DNS && LWIP_SOCKET
-
-#include "lwip/err.h"
-#include "lwip/mem.h"
-#include "lwip/memp.h"
-#include "lwip/ip_addr.h"
-#include "lwip/api.h"
-#include "lwip/dns.h"
-
-#include <string.h> /* memset */
-#include <stdlib.h> /* atoi */
-
-/** helper struct for gethostbyname_r to access the char* buffer */
-struct gethostbyname_r_helper {
-  ip_addr_t *addr_list[2];
-  ip_addr_t addr;
-  char *aliases;
-};
-
-/** h_errno is exported in netdb.h for access by applications. */
-#if LWIP_DNS_API_DECLARE_H_ERRNO
-int h_errno;
-#endif /* LWIP_DNS_API_DECLARE_H_ERRNO */
-
-/** define "hostent" variables storage: 0 if we use a static (but unprotected)
- * set of variables for lwip_gethostbyname, 1 if we use a local storage */
-#ifndef LWIP_DNS_API_HOSTENT_STORAGE
-#define LWIP_DNS_API_HOSTENT_STORAGE 0
-#endif
-
-/** define "hostent" variables storage */
-#if LWIP_DNS_API_HOSTENT_STORAGE
-#define HOSTENT_STORAGE
-#else
-#define HOSTENT_STORAGE static
-#endif /* LWIP_DNS_API_STATIC_HOSTENT */
-
-/**
- * Returns an entry containing addresses of address family AF_INET
- * for the host with name name.
- * Due to dns_gethostbyname limitations, only one address is returned.
- *
- * @param name the hostname to resolve
- * @return an entry containing addresses of address family AF_INET
- *         for the host with name name
- */
-struct hostent *
-lwip_gethostbyname(const char *name)
-{
-  err_t err;
-  ip_addr_t addr;
-
-  /* buffer variables for lwip_gethostbyname() */
-  HOSTENT_STORAGE struct hostent s_hostent;
-  HOSTENT_STORAGE char *s_aliases;
-  HOSTENT_STORAGE ip_addr_t s_hostent_addr;
-  HOSTENT_STORAGE ip_addr_t *s_phostent_addr[2];
-  HOSTENT_STORAGE char s_hostname[DNS_MAX_NAME_LENGTH + 1];
-
-  /* query host IP address */
-  err = netconn_gethostbyname(name, &addr);
-  if (err != ERR_OK) {
-    LWIP_DEBUGF(DNS_DEBUG, ("lwip_gethostbyname(%s) failed, err=%d\n", name, err));
-    h_errno = HOST_NOT_FOUND;
-    return NULL;
-  }
-
-  /* fill hostent */
-  s_hostent_addr = addr;
-  s_phostent_addr[0] = &s_hostent_addr;
-  s_phostent_addr[1] = NULL;
-  strncpy(s_hostname, name, DNS_MAX_NAME_LENGTH);
-  s_hostname[DNS_MAX_NAME_LENGTH] = 0;
-  s_hostent.h_name = s_hostname;
-  s_aliases = NULL;
-  s_hostent.h_aliases = &s_aliases;
-  s_hostent.h_addrtype = AF_INET;
-  s_hostent.h_length = sizeof(ip_addr_t);
-  s_hostent.h_addr_list = (char **)&s_phostent_addr;
-
-#if DNS_DEBUG
-  /* dump hostent */
-  LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_name           == %s\n", s_hostent.h_name));
-  LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_aliases        == %p\n", (void *)s_hostent.h_aliases));
-  /* h_aliases are always empty */
-  LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addrtype       == %d\n", s_hostent.h_addrtype));
-  LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_length         == %d\n", s_hostent.h_length));
-  LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list      == %p\n", (void *)s_hostent.h_addr_list));
-  if (s_hostent.h_addr_list != NULL) {
-    u8_t idx;
-    for (idx = 0; s_hostent.h_addr_list[idx]; idx++) {
-      LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list[%i]   == %p\n", idx, s_hostent.h_addr_list[idx]));
-      LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list[%i]-> == %s\n", idx, ipaddr_ntoa((ip_addr_t *)s_hostent.h_addr_list[idx])));
-    }
-  }
-#endif /* DNS_DEBUG */
-
-#if LWIP_DNS_API_HOSTENT_STORAGE
-  /* this function should return the "per-thread" hostent after copy from s_hostent */
-  return sys_thread_hostent(&s_hostent);
-#else
-  return &s_hostent;
-#endif /* LWIP_DNS_API_HOSTENT_STORAGE */
-}
-
-/**
- * Thread-safe variant of lwip_gethostbyname: instead of using a static
- * buffer, this function takes buffer and errno pointers as arguments
- * and uses these for the result.
- *
- * @param name the hostname to resolve
- * @param ret pre-allocated struct where to store the result
- * @param buf pre-allocated buffer where to store additional data
- * @param buflen the size of buf
- * @param result pointer to a hostent pointer that is set to ret on success
- *               and set to zero on error
- * @param h_errnop pointer to an int where to store errors (instead of modifying
- *                 the global h_errno)
- * @return 0 on success, non-zero on error, additional error information
- *         is stored in *h_errnop instead of h_errno to be thread-safe
- */
-int
-lwip_gethostbyname_r(const char *name, struct hostent *ret, char *buf,
-                     size_t buflen, struct hostent **result, int *h_errnop)
-{
-  err_t err;
-  struct gethostbyname_r_helper *h;
-  char *hostname;
-  size_t namelen;
-  int lh_errno;
-
-  if (h_errnop == NULL) {
-    /* ensure h_errnop is never NULL */
-    h_errnop = &lh_errno;
-  }
-
-  if (result == NULL) {
-    /* not all arguments given */
-    *h_errnop = EINVAL;
-    return -1;
-  }
-  /* first thing to do: set *result to nothing */
-  *result = NULL;
-  if ((name == NULL) || (ret == NULL) || (buf == NULL)) {
-    /* not all arguments given */
-    *h_errnop = EINVAL;
-    return -1;
-  }
-
-  namelen = strlen(name);
-  if (buflen < (sizeof(struct gethostbyname_r_helper) + LWIP_MEM_ALIGN_BUFFER(namelen + 1))) {
-    /* buf can't hold the data needed + a copy of name */
-    *h_errnop = ERANGE;
-    return -1;
-  }
-
-  h = (struct gethostbyname_r_helper *)LWIP_MEM_ALIGN(buf);
-  hostname = ((char *)h) + sizeof(struct gethostbyname_r_helper);
-
-  /* query host IP address */
-  err = netconn_gethostbyname(name, &h->addr);
-  if (err != ERR_OK) {
-    LWIP_DEBUGF(DNS_DEBUG, ("lwip_gethostbyname(%s) failed, err=%d\n", name, err));
-    *h_errnop = HOST_NOT_FOUND;
-    return -1;
-  }
-
-  /* copy the hostname into buf */
-  MEMCPY(hostname, name, namelen);
-  hostname[namelen] = 0;
-
-  /* fill hostent */
-  h->addr_list[0] = &h->addr;
-  h->addr_list[1] = NULL;
-  h->aliases = NULL;
-  ret->h_name = hostname;
-  ret->h_aliases = &h->aliases;
-  ret->h_addrtype = AF_INET;
-  ret->h_length = sizeof(ip_addr_t);
-  ret->h_addr_list = (char **)&h->addr_list;
-
-  /* set result != NULL */
-  *result = ret;
-
-  /* return success */
-  return 0;
-}
-
-/**
- * Frees one or more addrinfo structures returned by getaddrinfo(), along with
- * any additional storage associated with those structures. If the ai_next field
- * of the structure is not null, the entire list of structures is freed.
- *
- * @param ai struct addrinfo to free
- */
-void
-lwip_freeaddrinfo(struct addrinfo *ai)
-{
-  struct addrinfo *next;
-
-  while (ai != NULL) {
-    next = ai->ai_next;
-    memp_free(MEMP_NETDB, ai);
-    ai = next;
-  }
-}
-
-/**
- * Translates the name of a service location (for example, a host name) and/or
- * a service name and returns a set of socket addresses and associated
- * information to be used in creating a socket with which to address the
- * specified service.
- * Memory for the result is allocated internally and must be freed by calling
- * lwip_freeaddrinfo()!
- *
- * Due to a limitation in dns_gethostbyname, only the first address of a
- * host is returned.
- * Also, service names are not supported (only port numbers)!
- *
- * @param nodename descriptive name or address string of the host
- *                 (may be NULL -> local address)
- * @param servname port number as string of NULL
- * @param hints structure containing input values that set socktype and protocol
- * @param res pointer to a pointer where to store the result (set to NULL on failure)
- * @return 0 on success, non-zero on failure
- *
- * @todo: implement AI_V4MAPPED, AI_ADDRCONFIG
- */
-int
-lwip_getaddrinfo(const char *nodename, const char *servname,
-                 const struct addrinfo *hints, struct addrinfo **res)
-{
-  err_t err;
-  ip_addr_t addr;
-  struct addrinfo *ai;
-  struct sockaddr_storage *sa = NULL;
-  int port_nr = 0;
-  size_t total_size;
-  size_t namelen = 0;
-  int ai_family;
-
-  if (res == NULL) {
-    return EAI_FAIL;
-  }
-  *res = NULL;
-  if ((nodename == NULL) && (servname == NULL)) {
-    return EAI_NONAME;
-  }
-
-  if (hints != NULL) {
-    ai_family = hints->ai_family;
-    if ((ai_family != AF_UNSPEC)
-#if LWIP_IPV4
-        && (ai_family != AF_INET)
-#endif /* LWIP_IPV4 */
-#if LWIP_IPV6
-        && (ai_family != AF_INET6)
-#endif /* LWIP_IPV6 */
-       ) {
-      return EAI_FAMILY;
-    }
-  } else {
-    ai_family = AF_UNSPEC;
-  }
-
-  if (servname != NULL) {
-    /* service name specified: convert to port number
-     * @todo?: currently, only ASCII integers (port numbers) are supported (AI_NUMERICSERV)! */
-    port_nr = atoi(servname);
-    if ((port_nr <= 0) || (port_nr > 0xffff)) {
-      return EAI_SERVICE;
-    }
-  }
-
-  if (nodename != NULL) {
-    /* service location specified, try to resolve */
-    if ((hints != NULL) && (hints->ai_flags & AI_NUMERICHOST)) {
-      /* no DNS lookup, just parse for an address string */
-      if (!ipaddr_aton(nodename, &addr)) {
-        return EAI_NONAME;
-      }
-#if LWIP_IPV4 && LWIP_IPV6
-      if ((IP_IS_V6_VAL(addr) && ai_family == AF_INET) ||
-          (IP_IS_V4_VAL(addr) && ai_family == AF_INET6)) {
-        return EAI_NONAME;
-      }
-#endif /* LWIP_IPV4 && LWIP_IPV6 */
-    } else {
-#if LWIP_IPV4 && LWIP_IPV6
-      /* AF_UNSPEC: prefer IPv4 */
-      u8_t type = NETCONN_DNS_IPV4_IPV6;
-      if (ai_family == AF_INET) {
-        type = NETCONN_DNS_IPV4;
-      } else if (ai_family == AF_INET6) {
-        type = NETCONN_DNS_IPV6;
-      }
-#endif /* LWIP_IPV4 && LWIP_IPV6 */
-      err = netconn_gethostbyname_addrtype(nodename, &addr, type);
-      if (err != ERR_OK) {
-        return EAI_FAIL;
-      }
-    }
-  } else {
-    /* service location specified, use loopback address */
-    if ((hints != NULL) && (hints->ai_flags & AI_PASSIVE)) {
-      ip_addr_set_any_val(ai_family == AF_INET6, addr);
-    } else {
-      ip_addr_set_loopback_val(ai_family == AF_INET6, addr);
-    }
-  }
-
-  total_size = sizeof(struct addrinfo) + sizeof(struct sockaddr_storage);
-  if (nodename != NULL) {
-    namelen = strlen(nodename);
-    if (namelen > DNS_MAX_NAME_LENGTH) {
-      /* invalid name length */
-      return EAI_FAIL;
-    }
-    LWIP_ASSERT("namelen is too long", total_size + namelen + 1 > total_size);
-    total_size += namelen + 1;
-  }
-  /* If this fails, please report to lwip-devel! :-) */
-  LWIP_ASSERT("total_size <= NETDB_ELEM_SIZE: please report this!",
-              total_size <= NETDB_ELEM_SIZE);
-  ai = (struct addrinfo *)memp_malloc(MEMP_NETDB);
-  if (ai == NULL) {
-    return EAI_MEMORY;
-  }
-  memset(ai, 0, total_size);
-  /* cast through void* to get rid of alignment warnings */
-  sa = (struct sockaddr_storage *)(void *)((u8_t *)ai + sizeof(struct addrinfo));
-  if (IP_IS_V6_VAL(addr)) {
-#if LWIP_IPV6
-    struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sa;
-    /* set up sockaddr */
-    inet6_addr_from_ip6addr(&sa6->sin6_addr, ip_2_ip6(&addr));
-    sa6->sin6_family = AF_INET6;
-    sa6->sin6_len = sizeof(struct sockaddr_in6);
-    sa6->sin6_port = lwip_htons((u16_t)port_nr);
-    sa6->sin6_scope_id = ip6_addr_zone(ip_2_ip6(&addr));
-    ai->ai_family = AF_INET6;
-#endif /* LWIP_IPV6 */
-  } else {
-#if LWIP_IPV4
-    struct sockaddr_in *sa4 = (struct sockaddr_in *)sa;
-    /* set up sockaddr */
-    inet_addr_from_ip4addr(&sa4->sin_addr, ip_2_ip4(&addr));
-    sa4->sin_family = AF_INET;
-    sa4->sin_len = sizeof(struct sockaddr_in);
-    sa4->sin_port = lwip_htons((u16_t)port_nr);
-    ai->ai_family = AF_INET;
-#endif /* LWIP_IPV4 */
-  }
-
-  /* set up addrinfo */
-  if (hints != NULL) {
-    /* copy socktype & protocol from hints if specified */
-    ai->ai_socktype = hints->ai_socktype;
-    ai->ai_protocol = hints->ai_protocol;
-  }
-  if (nodename != NULL) {
-    /* copy nodename to canonname if specified */
-    ai->ai_canonname = ((char *)ai + sizeof(struct addrinfo) + sizeof(struct sockaddr_storage));
-    MEMCPY(ai->ai_canonname, nodename, namelen);
-    ai->ai_canonname[namelen] = 0;
-  }
-  ai->ai_addrlen = sizeof(struct sockaddr_storage);
-  ai->ai_addr = (struct sockaddr *)sa;
-
-  *res = ai;
-
-  return 0;
-}
-
-#endif /* LWIP_DNS && LWIP_SOCKET */
+/**
+ * @file
+ * API functions for name resolving
+ *
+ * @defgroup netdbapi NETDB API
+ * @ingroup socket
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt
+ *
+ */
+
+#include "lwip/netdb.h"
+
+#if LWIP_DNS && LWIP_SOCKET
+
+#include "lwip/err.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/ip_addr.h"
+#include "lwip/api.h"
+#include "lwip/dns.h"
+
+#include <string.h> /* memset */
+#include <stdlib.h> /* atoi */
+
+/** helper struct for gethostbyname_r to access the char* buffer */
+struct gethostbyname_r_helper {
+  ip_addr_t *addr_list[2];
+  ip_addr_t addr;
+  char *aliases;
+};
+
+/** h_errno is exported in netdb.h for access by applications. */
+#if LWIP_DNS_API_DECLARE_H_ERRNO
+int h_errno;
+#endif /* LWIP_DNS_API_DECLARE_H_ERRNO */
+
+/** define "hostent" variables storage: 0 if we use a static (but unprotected)
+ * set of variables for lwip_gethostbyname, 1 if we use a local storage */
+#ifndef LWIP_DNS_API_HOSTENT_STORAGE
+#define LWIP_DNS_API_HOSTENT_STORAGE 0
+#endif
+
+/** define "hostent" variables storage */
+#if LWIP_DNS_API_HOSTENT_STORAGE
+#define HOSTENT_STORAGE
+#else
+#define HOSTENT_STORAGE static
+#endif /* LWIP_DNS_API_STATIC_HOSTENT */
+
+/**
+ * Returns an entry containing addresses of address family AF_INET
+ * for the host with name name.
+ * Due to dns_gethostbyname limitations, only one address is returned.
+ *
+ * @param name the hostname to resolve
+ * @return an entry containing addresses of address family AF_INET
+ *         for the host with name name
+ */
+struct hostent*
+lwip_gethostbyname(const char *name)
+{
+  err_t err;
+  ip_addr_t addr;
+
+  /* buffer variables for lwip_gethostbyname() */
+  HOSTENT_STORAGE struct hostent s_hostent;
+  HOSTENT_STORAGE char *s_aliases;
+  HOSTENT_STORAGE ip_addr_t s_hostent_addr;
+  HOSTENT_STORAGE ip_addr_t *s_phostent_addr[2];
+  HOSTENT_STORAGE char s_hostname[DNS_MAX_NAME_LENGTH + 1];
+
+  /* query host IP address */
+  err = netconn_gethostbyname(name, &addr);
+  if (err != ERR_OK) {
+    LWIP_DEBUGF(DNS_DEBUG, ("lwip_gethostbyname(%s) failed, err=%d\n", name, err));
+    h_errno = HOST_NOT_FOUND;
+    return NULL;
+  }
+
+  /* fill hostent */
+  s_hostent_addr = addr;
+  s_phostent_addr[0] = &s_hostent_addr;
+  s_phostent_addr[1] = NULL;
+  strncpy(s_hostname, name, DNS_MAX_NAME_LENGTH);
+  s_hostname[DNS_MAX_NAME_LENGTH] = 0;
+  s_hostent.h_name = s_hostname;
+  s_aliases = NULL;
+  s_hostent.h_aliases = &s_aliases;
+  s_hostent.h_addrtype = AF_INET;
+  s_hostent.h_length = sizeof(ip_addr_t);
+  s_hostent.h_addr_list = (char**)&s_phostent_addr;
+
+#if DNS_DEBUG
+  /* dump hostent */
+  LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_name           == %s\n", s_hostent.h_name));
+  LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_aliases        == %p\n", (void*)s_hostent.h_aliases));
+  /* h_aliases are always empty */
+  LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addrtype       == %d\n", s_hostent.h_addrtype));
+  LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_length         == %d\n", s_hostent.h_length));
+  LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list      == %p\n", (void*)s_hostent.h_addr_list));
+  if (s_hostent.h_addr_list != NULL) {
+    u8_t idx;
+    for (idx=0; s_hostent.h_addr_list[idx]; idx++) {
+      LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list[%i]   == %p\n", idx, s_hostent.h_addr_list[idx]));
+      LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list[%i]-> == %s\n", idx, ipaddr_ntoa((ip_addr_t*)s_hostent.h_addr_list[idx])));
+    }
+  }
+#endif /* DNS_DEBUG */
+
+#if LWIP_DNS_API_HOSTENT_STORAGE
+  /* this function should return the "per-thread" hostent after copy from s_hostent */
+  return sys_thread_hostent(&s_hostent);
+#else
+  return &s_hostent;
+#endif /* LWIP_DNS_API_HOSTENT_STORAGE */
+}
+
+/**
+ * Thread-safe variant of lwip_gethostbyname: instead of using a static
+ * buffer, this function takes buffer and errno pointers as arguments
+ * and uses these for the result.
+ *
+ * @param name the hostname to resolve
+ * @param ret pre-allocated struct where to store the result
+ * @param buf pre-allocated buffer where to store additional data
+ * @param buflen the size of buf
+ * @param result pointer to a hostent pointer that is set to ret on success
+ *               and set to zero on error
+ * @param h_errnop pointer to an int where to store errors (instead of modifying
+ *                 the global h_errno)
+ * @return 0 on success, non-zero on error, additional error information
+ *         is stored in *h_errnop instead of h_errno to be thread-safe
+ */
+int
+lwip_gethostbyname_r(const char *name, struct hostent *ret, char *buf,
+                size_t buflen, struct hostent **result, int *h_errnop)
+{
+  err_t err;
+  struct gethostbyname_r_helper *h;
+  char *hostname;
+  size_t namelen;
+  int lh_errno;
+
+  if (h_errnop == NULL) {
+    /* ensure h_errnop is never NULL */
+    h_errnop = &lh_errno;
+  }
+
+  if (result == NULL) {
+    /* not all arguments given */
+    *h_errnop = EINVAL;
+    return -1;
+  }
+  /* first thing to do: set *result to nothing */
+  *result = NULL;
+  if ((name == NULL) || (ret == NULL) || (buf == NULL)) {
+    /* not all arguments given */
+    *h_errnop = EINVAL;
+    return -1;
+  }
+
+  namelen = strlen(name);
+  if (buflen < (sizeof(struct gethostbyname_r_helper) + namelen + 1 + (MEM_ALIGNMENT - 1))) {
+    /* buf can't hold the data needed + a copy of name */
+    *h_errnop = ERANGE;
+    return -1;
+  }
+
+  h = (struct gethostbyname_r_helper*)LWIP_MEM_ALIGN(buf);
+  hostname = ((char*)h) + sizeof(struct gethostbyname_r_helper);
+
+  /* query host IP address */
+  err = netconn_gethostbyname(name, &h->addr);
+  if (err != ERR_OK) {
+    LWIP_DEBUGF(DNS_DEBUG, ("lwip_gethostbyname(%s) failed, err=%d\n", name, err));
+    *h_errnop = HOST_NOT_FOUND;
+    return -1;
+  }
+
+  /* copy the hostname into buf */
+  MEMCPY(hostname, name, namelen);
+  hostname[namelen] = 0;
+
+  /* fill hostent */
+  h->addr_list[0] = &h->addr;
+  h->addr_list[1] = NULL;
+  h->aliases = NULL;
+  ret->h_name = hostname;
+  ret->h_aliases = &h->aliases;
+  ret->h_addrtype = AF_INET;
+  ret->h_length = sizeof(ip_addr_t);
+  ret->h_addr_list = (char**)&h->addr_list;
+
+  /* set result != NULL */
+  *result = ret;
+
+  /* return success */
+  return 0;
+}
+
+/**
+ * Frees one or more addrinfo structures returned by getaddrinfo(), along with
+ * any additional storage associated with those structures. If the ai_next field
+ * of the structure is not null, the entire list of structures is freed.
+ *
+ * @param ai struct addrinfo to free
+ */
+void
+lwip_freeaddrinfo(struct addrinfo *ai)
+{
+  struct addrinfo *next;
+
+  while (ai != NULL) {
+    next = ai->ai_next;
+    memp_free(MEMP_NETDB, ai);
+    ai = next;
+  }
+}
+
+/**
+ * Translates the name of a service location (for example, a host name) and/or
+ * a service name and returns a set of socket addresses and associated
+ * information to be used in creating a socket with which to address the
+ * specified service.
+ * Memory for the result is allocated internally and must be freed by calling
+ * lwip_freeaddrinfo()!
+ *
+ * Due to a limitation in dns_gethostbyname, only the first address of a
+ * host is returned.
+ * Also, service names are not supported (only port numbers)!
+ *
+ * @param nodename descriptive name or address string of the host
+ *                 (may be NULL -> local address)
+ * @param servname port number as string of NULL
+ * @param hints structure containing input values that set socktype and protocol
+ * @param res pointer to a pointer where to store the result (set to NULL on failure)
+ * @return 0 on success, non-zero on failure
+ *
+ * @todo: implement AI_V4MAPPED, AI_ADDRCONFIG
+ */
+int
+lwip_getaddrinfo(const char *nodename, const char *servname,
+       const struct addrinfo *hints, struct addrinfo **res)
+{
+  err_t err;
+  ip_addr_t addr;
+  struct addrinfo *ai;
+  struct sockaddr_storage *sa = NULL;
+  int port_nr = 0;
+  size_t total_size;
+  size_t namelen = 0;
+  int ai_family;
+
+  if (res == NULL) {
+    return EAI_FAIL;
+  }
+  *res = NULL;
+  if ((nodename == NULL) && (servname == NULL)) {
+    return EAI_NONAME;
+  }
+
+  if (hints != NULL) {
+    ai_family = hints->ai_family;
+    if ((ai_family != AF_UNSPEC)
+#if LWIP_IPV4
+      && (ai_family != AF_INET)
+#endif /* LWIP_IPV4 */
+#if LWIP_IPV6
+      && (ai_family != AF_INET6)
+#endif /* LWIP_IPV6 */
+      ) {
+      return EAI_FAMILY;
+    }
+  } else {
+    ai_family = AF_UNSPEC;
+  }
+
+  if (servname != NULL) {
+    /* service name specified: convert to port number
+     * @todo?: currently, only ASCII integers (port numbers) are supported (AI_NUMERICSERV)! */
+    port_nr = atoi(servname);
+    if ((port_nr <= 0) || (port_nr > 0xffff)) {
+      return EAI_SERVICE;
+    }
+  }
+
+  if (nodename != NULL) {
+    /* service location specified, try to resolve */
+    if ((hints != NULL) && (hints->ai_flags & AI_NUMERICHOST)) {
+      /* no DNS lookup, just parse for an address string */
+      if (!ipaddr_aton(nodename, &addr)) {
+        return EAI_NONAME;
+      }
+#if LWIP_IPV4 && LWIP_IPV6
+      if ((IP_IS_V6_VAL(addr) && ai_family == AF_INET) ||
+          (IP_IS_V4_VAL(addr) && ai_family == AF_INET6)) {
+        return EAI_NONAME;
+      }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+    } else {
+#if LWIP_IPV4 && LWIP_IPV6
+      /* AF_UNSPEC: prefer IPv4 */
+      u8_t type = NETCONN_DNS_IPV4_IPV6;
+      if (ai_family == AF_INET) {
+        type = NETCONN_DNS_IPV4;
+      } else if (ai_family == AF_INET6) {
+        type = NETCONN_DNS_IPV6;
+      }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+      err = netconn_gethostbyname_addrtype(nodename, &addr, type);
+      if (err != ERR_OK) {
+        return EAI_FAIL;
+      }
+    }
+  } else {
+    /* service location specified, use loopback address */
+    if ((hints != NULL) && (hints->ai_flags & AI_PASSIVE)) {
+      ip_addr_set_any(ai_family == AF_INET6, &addr);
+    } else {
+      ip_addr_set_loopback(ai_family == AF_INET6, &addr);
+    }
+  }
+
+  total_size = sizeof(struct addrinfo) + sizeof(struct sockaddr_storage);
+  if (nodename != NULL) {
+    namelen = strlen(nodename);
+    if (namelen > DNS_MAX_NAME_LENGTH) {
+      /* invalid name length */
+      return EAI_FAIL;
+    }
+    LWIP_ASSERT("namelen is too long", total_size + namelen + 1 > total_size);
+    total_size += namelen + 1;
+  }
+  /* If this fails, please report to lwip-devel! :-) */
+  LWIP_ASSERT("total_size <= NETDB_ELEM_SIZE: please report this!",
+    total_size <= NETDB_ELEM_SIZE);
+  ai = (struct addrinfo *)memp_malloc(MEMP_NETDB);
+  if (ai == NULL) {
+    return EAI_MEMORY;
+  }
+  memset(ai, 0, total_size);
+  /* cast through void* to get rid of alignment warnings */
+  sa = (struct sockaddr_storage *)(void*)((u8_t*)ai + sizeof(struct addrinfo));
+  if (IP_IS_V6_VAL(addr)) {
+#if LWIP_IPV6
+    struct sockaddr_in6 *sa6 = (struct sockaddr_in6*)sa;
+    /* set up sockaddr */
+    inet6_addr_from_ip6addr(&sa6->sin6_addr, ip_2_ip6(&addr));
+    sa6->sin6_family = AF_INET6;
+    sa6->sin6_len = sizeof(struct sockaddr_in6);
+    sa6->sin6_port = lwip_htons((u16_t)port_nr);
+    ai->ai_family = AF_INET6;
+#endif /* LWIP_IPV6 */
+  } else {
+#if LWIP_IPV4
+    struct sockaddr_in *sa4 = (struct sockaddr_in*)sa;
+    /* set up sockaddr */
+    inet_addr_from_ip4addr(&sa4->sin_addr, ip_2_ip4(&addr));
+    sa4->sin_family = AF_INET;
+    sa4->sin_len = sizeof(struct sockaddr_in);
+    sa4->sin_port = lwip_htons((u16_t)port_nr);
+    ai->ai_family = AF_INET;
+#endif /* LWIP_IPV4 */
+  }
+
+  /* set up addrinfo */
+  if (hints != NULL) {
+    /* copy socktype & protocol from hints if specified */
+    ai->ai_socktype = hints->ai_socktype;
+    ai->ai_protocol = hints->ai_protocol;
+  }
+  if (nodename != NULL) {
+    /* copy nodename to canonname if specified */
+    ai->ai_canonname = ((char*)ai + sizeof(struct addrinfo) + sizeof(struct sockaddr_storage));
+    MEMCPY(ai->ai_canonname, nodename, namelen);
+    ai->ai_canonname[namelen] = 0;
+  }
+  ai->ai_addrlen = sizeof(struct sockaddr_storage);
+  ai->ai_addr = (struct sockaddr*)sa;
+
+  *res = ai;
+
+  return 0;
+}
+
+#endif /* LWIP_DNS && LWIP_SOCKET */

+ 221 - 380
libs/thirdparty/lwip_2.1.2/src/api/netifapi.c → libs/thirdparty/LwIP/src/api/netifapi.c

@@ -1,380 +1,221 @@
-/**
- * @file
- * Network Interface Sequential API module
- *
- * @defgroup netifapi NETIF API
- * @ingroup sequential_api
- * Thread-safe functions to be called from non-TCPIP threads
- *
- * @defgroup netifapi_netif NETIF related
- * @ingroup netifapi
- * To be called from non-TCPIP threads
- */
-
-/*
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- *
- */
-
-#include "lwip/opt.h"
-
-#if LWIP_NETIF_API /* don't build if not configured for use in lwipopts.h */
-
-#include "lwip/etharp.h"
-#include "lwip/netifapi.h"
-#include "lwip/memp.h"
-#include "lwip/priv/tcpip_priv.h"
-
-#include <string.h> /* strncpy */
-
-#define NETIFAPI_VAR_REF(name)      API_VAR_REF(name)
-#define NETIFAPI_VAR_DECLARE(name)  API_VAR_DECLARE(struct netifapi_msg, name)
-#define NETIFAPI_VAR_ALLOC(name)    API_VAR_ALLOC(struct netifapi_msg, MEMP_NETIFAPI_MSG, name, ERR_MEM)
-#define NETIFAPI_VAR_FREE(name)     API_VAR_FREE(MEMP_NETIFAPI_MSG, name)
-
-/**
- * Call netif_add() inside the tcpip_thread context.
- */
-static err_t
-netifapi_do_netif_add(struct tcpip_api_call_data *m)
-{
-  /* cast through void* to silence alignment warnings.
-   * We know it works because the structs have been instantiated as struct netifapi_msg */
-  struct netifapi_msg *msg = (struct netifapi_msg *)(void *)m;
-
-  if (!netif_add( msg->netif,
-#if LWIP_IPV4
-                  API_EXPR_REF(msg->msg.add.ipaddr),
-                  API_EXPR_REF(msg->msg.add.netmask),
-                  API_EXPR_REF(msg->msg.add.gw),
-#endif /* LWIP_IPV4 */
-                  msg->msg.add.state,
-                  msg->msg.add.init,
-                  msg->msg.add.input)) {
-    return ERR_IF;
-  } else {
-    return ERR_OK;
-  }
-}
-
-#if LWIP_IPV4
-/**
- * Call netif_set_addr() inside the tcpip_thread context.
- */
-static err_t
-netifapi_do_netif_set_addr(struct tcpip_api_call_data *m)
-{
-  /* cast through void* to silence alignment warnings.
-   * We know it works because the structs have been instantiated as struct netifapi_msg */
-  struct netifapi_msg *msg = (struct netifapi_msg *)(void *)m;
-
-  netif_set_addr( msg->netif,
-                  API_EXPR_REF(msg->msg.add.ipaddr),
-                  API_EXPR_REF(msg->msg.add.netmask),
-                  API_EXPR_REF(msg->msg.add.gw));
-  return ERR_OK;
-}
-#endif /* LWIP_IPV4 */
-
-/**
-* Call netif_name_to_index() inside the tcpip_thread context.
-*/
-static err_t
-netifapi_do_name_to_index(struct tcpip_api_call_data *m)
-{
-  /* cast through void* to silence alignment warnings.
-   * We know it works because the structs have been instantiated as struct netifapi_msg */
-  struct netifapi_msg *msg = (struct netifapi_msg *)(void *)m;
-
-  msg->msg.ifs.index = netif_name_to_index(msg->msg.ifs.name);
-  return ERR_OK;
-}
-
-/**
-* Call netif_index_to_name() inside the tcpip_thread context.
-*/
-static err_t
-netifapi_do_index_to_name(struct tcpip_api_call_data *m)
-{
-  /* cast through void* to silence alignment warnings.
-   * We know it works because the structs have been instantiated as struct netifapi_msg */
-  struct netifapi_msg *msg = (struct netifapi_msg *)(void *)m;
-
-  if (!netif_index_to_name(msg->msg.ifs.index, msg->msg.ifs.name)) {
-    /* return failure via empty name */
-    msg->msg.ifs.name[0] = '\0';
-  }
-  return ERR_OK;
-}
-
-/**
- * Call the "errtfunc" (or the "voidfunc" if "errtfunc" is NULL) inside the
- * tcpip_thread context.
- */
-static err_t
-netifapi_do_netif_common(struct tcpip_api_call_data *m)
-{
-  /* cast through void* to silence alignment warnings.
-   * We know it works because the structs have been instantiated as struct netifapi_msg */
-  struct netifapi_msg *msg = (struct netifapi_msg *)(void *)m;
-
-  if (msg->msg.common.errtfunc != NULL) {
-    return msg->msg.common.errtfunc(msg->netif);
-  } else {
-    msg->msg.common.voidfunc(msg->netif);
-    return ERR_OK;
-  }
-}
-
-#if LWIP_ARP && LWIP_IPV4
-/**
- * @ingroup netifapi_arp
- * Add or update an entry in the ARP cache.
- * For an update, ipaddr is used to find the cache entry.
- *
- * @param ipaddr IPv4 address of cache entry
- * @param ethaddr hardware address mapped to ipaddr
- * @param type type of ARP cache entry
- * @return ERR_OK: entry added/updated, else error from err_t
- */
-err_t
-netifapi_arp_add(const ip4_addr_t *ipaddr, struct eth_addr *ethaddr, enum netifapi_arp_entry type)
-{
-  err_t err;
-
-  /* We only support permanent entries currently */
-  LWIP_UNUSED_ARG(type);
-
-#if ETHARP_SUPPORT_STATIC_ENTRIES && LWIP_TCPIP_CORE_LOCKING
-  LOCK_TCPIP_CORE();
-  err = etharp_add_static_entry(ipaddr, ethaddr);
-  UNLOCK_TCPIP_CORE();
-#else
-  /* @todo add new vars to struct netifapi_msg and create a 'do' func */
-  LWIP_UNUSED_ARG(ipaddr);
-  LWIP_UNUSED_ARG(ethaddr);
-  err = ERR_VAL;
-#endif /* ETHARP_SUPPORT_STATIC_ENTRIES && LWIP_TCPIP_CORE_LOCKING */
-
-  return err;
-}
-
-/**
- * @ingroup netifapi_arp
- * Remove an entry in the ARP cache identified by ipaddr
- *
- * @param ipaddr IPv4 address of cache entry
- * @param type type of ARP cache entry
- * @return ERR_OK: entry removed, else error from err_t
- */
-err_t
-netifapi_arp_remove(const ip4_addr_t *ipaddr, enum netifapi_arp_entry type)
-{
-  err_t err;
-
-  /* We only support permanent entries currently */
-  LWIP_UNUSED_ARG(type);
-
-#if ETHARP_SUPPORT_STATIC_ENTRIES && LWIP_TCPIP_CORE_LOCKING
-  LOCK_TCPIP_CORE();
-  err = etharp_remove_static_entry(ipaddr);
-  UNLOCK_TCPIP_CORE();
-#else
-  /* @todo add new vars to struct netifapi_msg and create a 'do' func */
-  LWIP_UNUSED_ARG(ipaddr);
-  err = ERR_VAL;
-#endif /* ETHARP_SUPPORT_STATIC_ENTRIES && LWIP_TCPIP_CORE_LOCKING */
-
-  return err;
-}
-#endif /* LWIP_ARP && LWIP_IPV4 */
-
-/**
- * @ingroup netifapi_netif
- * Call netif_add() in a thread-safe way by running that function inside the
- * tcpip_thread context.
- *
- * @note for params @see netif_add()
- */
-err_t
-netifapi_netif_add(struct netif *netif,
-#if LWIP_IPV4
-                   const ip4_addr_t *ipaddr, const ip4_addr_t *netmask, const ip4_addr_t *gw,
-#endif /* LWIP_IPV4 */
-                   void *state, netif_init_fn init, netif_input_fn input)
-{
-  err_t err;
-  NETIFAPI_VAR_DECLARE(msg);
-  NETIFAPI_VAR_ALLOC(msg);
-
-#if LWIP_IPV4
-  if (ipaddr == NULL) {
-    ipaddr = IP4_ADDR_ANY4;
-  }
-  if (netmask == NULL) {
-    netmask = IP4_ADDR_ANY4;
-  }
-  if (gw == NULL) {
-    gw = IP4_ADDR_ANY4;
-  }
-#endif /* LWIP_IPV4 */
-
-  NETIFAPI_VAR_REF(msg).netif = netif;
-#if LWIP_IPV4
-  NETIFAPI_VAR_REF(msg).msg.add.ipaddr  = NETIFAPI_VAR_REF(ipaddr);
-  NETIFAPI_VAR_REF(msg).msg.add.netmask = NETIFAPI_VAR_REF(netmask);
-  NETIFAPI_VAR_REF(msg).msg.add.gw      = NETIFAPI_VAR_REF(gw);
-#endif /* LWIP_IPV4 */
-  NETIFAPI_VAR_REF(msg).msg.add.state   = state;
-  NETIFAPI_VAR_REF(msg).msg.add.init    = init;
-  NETIFAPI_VAR_REF(msg).msg.add.input   = input;
-  err = tcpip_api_call(netifapi_do_netif_add, &API_VAR_REF(msg).call);
-  NETIFAPI_VAR_FREE(msg);
-  return err;
-}
-
-#if LWIP_IPV4
-/**
- * @ingroup netifapi_netif
- * Call netif_set_addr() in a thread-safe way by running that function inside the
- * tcpip_thread context.
- *
- * @note for params @see netif_set_addr()
- */
-err_t
-netifapi_netif_set_addr(struct netif *netif,
-                        const ip4_addr_t *ipaddr,
-                        const ip4_addr_t *netmask,
-                        const ip4_addr_t *gw)
-{
-  err_t err;
-  NETIFAPI_VAR_DECLARE(msg);
-  NETIFAPI_VAR_ALLOC(msg);
-
-  if (ipaddr == NULL) {
-    ipaddr = IP4_ADDR_ANY4;
-  }
-  if (netmask == NULL) {
-    netmask = IP4_ADDR_ANY4;
-  }
-  if (gw == NULL) {
-    gw = IP4_ADDR_ANY4;
-  }
-
-  NETIFAPI_VAR_REF(msg).netif = netif;
-  NETIFAPI_VAR_REF(msg).msg.add.ipaddr  = NETIFAPI_VAR_REF(ipaddr);
-  NETIFAPI_VAR_REF(msg).msg.add.netmask = NETIFAPI_VAR_REF(netmask);
-  NETIFAPI_VAR_REF(msg).msg.add.gw      = NETIFAPI_VAR_REF(gw);
-  err = tcpip_api_call(netifapi_do_netif_set_addr, &API_VAR_REF(msg).call);
-  NETIFAPI_VAR_FREE(msg);
-  return err;
-}
-#endif /* LWIP_IPV4 */
-
-/**
- * call the "errtfunc" (or the "voidfunc" if "errtfunc" is NULL) in a thread-safe
- * way by running that function inside the tcpip_thread context.
- *
- * @note use only for functions where there is only "netif" parameter.
- */
-err_t
-netifapi_netif_common(struct netif *netif, netifapi_void_fn voidfunc,
-                      netifapi_errt_fn errtfunc)
-{
-  err_t err;
-  NETIFAPI_VAR_DECLARE(msg);
-  NETIFAPI_VAR_ALLOC(msg);
-
-  NETIFAPI_VAR_REF(msg).netif = netif;
-  NETIFAPI_VAR_REF(msg).msg.common.voidfunc = voidfunc;
-  NETIFAPI_VAR_REF(msg).msg.common.errtfunc = errtfunc;
-  err = tcpip_api_call(netifapi_do_netif_common, &API_VAR_REF(msg).call);
-  NETIFAPI_VAR_FREE(msg);
-  return err;
-}
-
-/**
-* @ingroup netifapi_netif
-* Call netif_name_to_index() in a thread-safe way by running that function inside the
-* tcpip_thread context.
-*
-* @param name the interface name of the netif
-* @param idx output index of the found netif
-*/
-err_t
-netifapi_netif_name_to_index(const char *name, u8_t *idx)
-{
-  err_t err;
-  NETIFAPI_VAR_DECLARE(msg);
-  NETIFAPI_VAR_ALLOC(msg);
-
-  *idx = 0;
-
-#if LWIP_MPU_COMPATIBLE
-  strncpy(NETIFAPI_VAR_REF(msg).msg.ifs.name, name, NETIF_NAMESIZE - 1);
-  NETIFAPI_VAR_REF(msg).msg.ifs.name[NETIF_NAMESIZE - 1] = '\0';
-#else
-  NETIFAPI_VAR_REF(msg).msg.ifs.name = LWIP_CONST_CAST(char *, name);
-#endif /* LWIP_MPU_COMPATIBLE */
-  err = tcpip_api_call(netifapi_do_name_to_index, &API_VAR_REF(msg).call);
-  if (!err) {
-    *idx = NETIFAPI_VAR_REF(msg).msg.ifs.index;
-  }
-  NETIFAPI_VAR_FREE(msg);
-  return err;
-}
-
-/**
-* @ingroup netifapi_netif
-* Call netif_index_to_name() in a thread-safe way by running that function inside the
-* tcpip_thread context.
-*
-* @param idx the interface index of the netif
-* @param name output name of the found netif, empty '\0' string if netif not found.
-*             name should be of at least NETIF_NAMESIZE bytes
-*/
-err_t
-netifapi_netif_index_to_name(u8_t idx, char *name)
-{
-  err_t err;
-  NETIFAPI_VAR_DECLARE(msg);
-  NETIFAPI_VAR_ALLOC(msg);
-
-  NETIFAPI_VAR_REF(msg).msg.ifs.index = idx;
-#if !LWIP_MPU_COMPATIBLE
-  NETIFAPI_VAR_REF(msg).msg.ifs.name = name;
-#endif /* LWIP_MPU_COMPATIBLE */
-  err = tcpip_api_call(netifapi_do_index_to_name, &API_VAR_REF(msg).call);
-#if LWIP_MPU_COMPATIBLE
-  if (!err) {
-    strncpy(name, NETIFAPI_VAR_REF(msg).msg.ifs.name, NETIF_NAMESIZE - 1);
-    name[NETIF_NAMESIZE - 1] = '\0';
-  }
-#endif /* LWIP_MPU_COMPATIBLE */
-  NETIFAPI_VAR_FREE(msg);
-  return err;
-}
-
-#endif /* LWIP_NETIF_API */
+/**
+ * @file
+ * Network Interface Sequential API module
+ *
+ * @defgroup netifapi NETIF API
+ * @ingroup sequential_api
+ * Thread-safe functions to be called from non-TCPIP threads
+ * 
+ * @defgroup netifapi_netif NETIF related
+ * @ingroup netifapi
+ * To be called from non-TCPIP threads 
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_NETIF_API /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/netifapi.h"
+#include "lwip/memp.h"
+#include "lwip/priv/tcpip_priv.h"
+
+#define NETIFAPI_VAR_REF(name)      API_VAR_REF(name)
+#define NETIFAPI_VAR_DECLARE(name)  API_VAR_DECLARE(struct netifapi_msg, name)
+#define NETIFAPI_VAR_ALLOC(name)    API_VAR_ALLOC(struct netifapi_msg, MEMP_NETIFAPI_MSG, name, ERR_MEM)
+#define NETIFAPI_VAR_FREE(name)     API_VAR_FREE(MEMP_NETIFAPI_MSG, name)
+
+/**
+ * Call netif_add() inside the tcpip_thread context.
+ */
+static err_t
+netifapi_do_netif_add(struct tcpip_api_call_data *m)
+{
+  /* cast through void* to silence alignment warnings. 
+   * We know it works because the structs have been instantiated as struct netifapi_msg */
+  struct netifapi_msg *msg = (struct netifapi_msg*)(void*)m;
+  
+  if (!netif_add( msg->netif,
+#if LWIP_IPV4
+                  API_EXPR_REF(msg->msg.add.ipaddr),
+                  API_EXPR_REF(msg->msg.add.netmask),
+                  API_EXPR_REF(msg->msg.add.gw),
+#endif /* LWIP_IPV4 */
+                  msg->msg.add.state,
+                  msg->msg.add.init,
+                  msg->msg.add.input)) {
+    return ERR_IF;
+  } else {
+    return ERR_OK;
+  }
+}
+
+#if LWIP_IPV4
+/**
+ * Call netif_set_addr() inside the tcpip_thread context.
+ */
+static err_t
+netifapi_do_netif_set_addr(struct tcpip_api_call_data *m)
+{
+  /* cast through void* to silence alignment warnings. 
+   * We know it works because the structs have been instantiated as struct netifapi_msg */
+  struct netifapi_msg *msg = (struct netifapi_msg*)(void*)m;
+
+  netif_set_addr( msg->netif,
+                  API_EXPR_REF(msg->msg.add.ipaddr),
+                  API_EXPR_REF(msg->msg.add.netmask),
+                  API_EXPR_REF(msg->msg.add.gw));
+  return ERR_OK;
+}
+#endif /* LWIP_IPV4 */
+
+/**
+ * Call the "errtfunc" (or the "voidfunc" if "errtfunc" is NULL) inside the
+ * tcpip_thread context.
+ */
+static err_t
+netifapi_do_netif_common(struct tcpip_api_call_data *m)
+{
+  /* cast through void* to silence alignment warnings. 
+   * We know it works because the structs have been instantiated as struct netifapi_msg */
+  struct netifapi_msg *msg = (struct netifapi_msg*)(void*)m;
+
+  if (msg->msg.common.errtfunc != NULL) {
+    return msg->msg.common.errtfunc(msg->netif);
+  } else {
+    msg->msg.common.voidfunc(msg->netif);
+    return ERR_OK;
+  }
+}
+
+/**
+ * @ingroup netifapi_netif
+ * Call netif_add() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ *
+ * @note for params @see netif_add()
+ */
+err_t
+netifapi_netif_add(struct netif *netif,
+#if LWIP_IPV4
+                   const ip4_addr_t *ipaddr, const ip4_addr_t *netmask, const ip4_addr_t *gw,
+#endif /* LWIP_IPV4 */
+                   void *state, netif_init_fn init, netif_input_fn input)
+{
+  err_t err;
+  NETIFAPI_VAR_DECLARE(msg);
+  NETIFAPI_VAR_ALLOC(msg);
+
+#if LWIP_IPV4
+  if (ipaddr == NULL) {
+    ipaddr = IP4_ADDR_ANY4;
+  }
+  if (netmask == NULL) {
+    netmask = IP4_ADDR_ANY4;
+  }
+  if (gw == NULL) {
+    gw = IP4_ADDR_ANY4;
+  }
+#endif /* LWIP_IPV4 */
+
+  NETIFAPI_VAR_REF(msg).netif = netif;
+#if LWIP_IPV4
+  NETIFAPI_VAR_REF(msg).msg.add.ipaddr  = NETIFAPI_VAR_REF(ipaddr);
+  NETIFAPI_VAR_REF(msg).msg.add.netmask = NETIFAPI_VAR_REF(netmask);
+  NETIFAPI_VAR_REF(msg).msg.add.gw      = NETIFAPI_VAR_REF(gw);
+#endif /* LWIP_IPV4 */
+  NETIFAPI_VAR_REF(msg).msg.add.state   = state;
+  NETIFAPI_VAR_REF(msg).msg.add.init    = init;
+  NETIFAPI_VAR_REF(msg).msg.add.input   = input;
+  err = tcpip_api_call(netifapi_do_netif_add, &API_VAR_REF(msg).call);
+  NETIFAPI_VAR_FREE(msg);
+  return err;
+}
+
+#if LWIP_IPV4
+/**
+ * @ingroup netifapi_netif
+ * Call netif_set_addr() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ *
+ * @note for params @see netif_set_addr()
+ */
+err_t
+netifapi_netif_set_addr(struct netif *netif,
+                        const ip4_addr_t *ipaddr,
+                        const ip4_addr_t *netmask,
+                        const ip4_addr_t *gw)
+{
+  err_t err;
+  NETIFAPI_VAR_DECLARE(msg);
+  NETIFAPI_VAR_ALLOC(msg);
+
+  if (ipaddr == NULL) {
+    ipaddr = IP4_ADDR_ANY4;
+  }
+  if (netmask == NULL) {
+    netmask = IP4_ADDR_ANY4;
+  }
+  if (gw == NULL) {
+    gw = IP4_ADDR_ANY4;
+  }
+
+  NETIFAPI_VAR_REF(msg).netif = netif;
+  NETIFAPI_VAR_REF(msg).msg.add.ipaddr  = NETIFAPI_VAR_REF(ipaddr);
+  NETIFAPI_VAR_REF(msg).msg.add.netmask = NETIFAPI_VAR_REF(netmask);
+  NETIFAPI_VAR_REF(msg).msg.add.gw      = NETIFAPI_VAR_REF(gw);
+  err = tcpip_api_call(netifapi_do_netif_set_addr, &API_VAR_REF(msg).call);
+  NETIFAPI_VAR_FREE(msg);
+  return err;
+}
+#endif /* LWIP_IPV4 */
+
+/**
+ * call the "errtfunc" (or the "voidfunc" if "errtfunc" is NULL) in a thread-safe
+ * way by running that function inside the tcpip_thread context.
+ *
+ * @note use only for functions where there is only "netif" parameter.
+ */
+err_t
+netifapi_netif_common(struct netif *netif, netifapi_void_fn voidfunc,
+                       netifapi_errt_fn errtfunc)
+{
+  err_t err;
+  NETIFAPI_VAR_DECLARE(msg);
+  NETIFAPI_VAR_ALLOC(msg);
+
+  NETIFAPI_VAR_REF(msg).netif = netif;
+  NETIFAPI_VAR_REF(msg).msg.common.voidfunc = voidfunc;
+  NETIFAPI_VAR_REF(msg).msg.common.errtfunc = errtfunc;
+  err = tcpip_api_call(netifapi_do_netif_common, &API_VAR_REF(msg).call);
+  NETIFAPI_VAR_FREE(msg);
+  return err;
+}
+
+#endif /* LWIP_NETIF_API */

+ 2828 - 0
libs/thirdparty/LwIP/src/api/sockets.c

@@ -0,0 +1,2828 @@
+/**
+ * @file
+ * Sockets BSD-Like API module
+ *
+ * @defgroup socket Socket API
+ * @ingroup sequential_api
+ * BSD-style socket API.\n
+ * Thread-safe, to be called from non-TCPIP threads only.\n
+ * Can be activated by defining @ref LWIP_SOCKET to 1.\n
+ * Header is in posix/sys/socket.h\b
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ * Improved by Marc Boucher <marc@mbsi.ca> and David Haas <dhaas@alum.rpi.edu>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/sockets.h"
+#include "lwip/api.h"
+#include "lwip/sys.h"
+#include "lwip/igmp.h"
+#include "lwip/inet.h"
+#include "lwip/tcp.h"
+#include "lwip/raw.h"
+#include "lwip/udp.h"
+#include "lwip/memp.h"
+#include "lwip/pbuf.h"
+#include "lwip/priv/tcpip_priv.h"
+#if LWIP_CHECKSUM_ON_COPY
+#include "lwip/inet_chksum.h"
+#endif
+
+#include <string.h>
+
+/* If the netconn API is not required publicly, then we include the necessary
+   files here to get the implementation */
+#if !LWIP_NETCONN
+#undef LWIP_NETCONN
+#define LWIP_NETCONN 1
+#include "api_msg.c"
+#include "api_lib.c"
+#include "netbuf.c"
+#undef LWIP_NETCONN
+#define LWIP_NETCONN 0
+#endif
+
+#if LWIP_IPV4
+#define IP4ADDR_PORT_TO_SOCKADDR(sin, ipaddr, port) do { \
+      (sin)->sin_len = sizeof(struct sockaddr_in); \
+      (sin)->sin_family = AF_INET; \
+      (sin)->sin_port = lwip_htons((port)); \
+      inet_addr_from_ip4addr(&(sin)->sin_addr, ipaddr); \
+      memset((sin)->sin_zero, 0, SIN_ZERO_LEN); }while(0)
+#define SOCKADDR4_TO_IP4ADDR_PORT(sin, ipaddr, port) do { \
+    inet_addr_to_ip4addr(ip_2_ip4(ipaddr), &((sin)->sin_addr)); \
+    (port) = lwip_ntohs((sin)->sin_port); }while(0)
+#endif /* LWIP_IPV4 */
+
+#if LWIP_IPV6
+#define IP6ADDR_PORT_TO_SOCKADDR(sin6, ipaddr, port) do { \
+      (sin6)->sin6_len = sizeof(struct sockaddr_in6); \
+      (sin6)->sin6_family = AF_INET6; \
+      (sin6)->sin6_port = lwip_htons((port)); \
+      (sin6)->sin6_flowinfo = 0; \
+      inet6_addr_from_ip6addr(&(sin6)->sin6_addr, ipaddr); \
+      (sin6)->sin6_scope_id = 0; }while(0)
+#define SOCKADDR6_TO_IP6ADDR_PORT(sin6, ipaddr, port) do { \
+    inet6_addr_to_ip6addr(ip_2_ip6(ipaddr), &((sin6)->sin6_addr)); \
+    (port) = lwip_ntohs((sin6)->sin6_port); }while(0)
+#endif /* LWIP_IPV6 */
+
+#if LWIP_IPV4 && LWIP_IPV6
+static void sockaddr_to_ipaddr_port(const struct sockaddr* sockaddr, ip_addr_t* ipaddr, u16_t* port);
+
+#define IS_SOCK_ADDR_LEN_VALID(namelen)  (((namelen) == sizeof(struct sockaddr_in)) || \
+                                         ((namelen) == sizeof(struct sockaddr_in6)))
+#define IS_SOCK_ADDR_TYPE_VALID(name)    (((name)->sa_family == AF_INET) || \
+                                         ((name)->sa_family == AF_INET6))
+#define SOCK_ADDR_TYPE_MATCH(name, sock) \
+       ((((name)->sa_family == AF_INET) && !(NETCONNTYPE_ISIPV6((sock)->conn->type))) || \
+       (((name)->sa_family == AF_INET6) && (NETCONNTYPE_ISIPV6((sock)->conn->type))))
+#define IPADDR_PORT_TO_SOCKADDR(sockaddr, ipaddr, port) do { \
+    if (IP_IS_V6(ipaddr)) { \
+      IP6ADDR_PORT_TO_SOCKADDR((struct sockaddr_in6*)(void*)(sockaddr), ip_2_ip6(ipaddr), port); \
+    } else { \
+      IP4ADDR_PORT_TO_SOCKADDR((struct sockaddr_in*)(void*)(sockaddr), ip_2_ip4(ipaddr), port); \
+    } } while(0)
+#define SOCKADDR_TO_IPADDR_PORT(sockaddr, ipaddr, port) sockaddr_to_ipaddr_port(sockaddr, ipaddr, &(port))
+#define DOMAIN_TO_NETCONN_TYPE(domain, type) (((domain) == AF_INET) ? \
+  (type) : (enum netconn_type)((type) | NETCONN_TYPE_IPV6))
+#elif LWIP_IPV6 /* LWIP_IPV4 && LWIP_IPV6 */
+#define IS_SOCK_ADDR_LEN_VALID(namelen)  ((namelen) == sizeof(struct sockaddr_in6))
+#define IS_SOCK_ADDR_TYPE_VALID(name)    ((name)->sa_family == AF_INET6)
+#define SOCK_ADDR_TYPE_MATCH(name, sock) 1
+#define IPADDR_PORT_TO_SOCKADDR(sockaddr, ipaddr, port) \
+        IP6ADDR_PORT_TO_SOCKADDR((struct sockaddr_in6*)(void*)(sockaddr), ip_2_ip6(ipaddr), port)
+#define SOCKADDR_TO_IPADDR_PORT(sockaddr, ipaddr, port) \
+        SOCKADDR6_TO_IP6ADDR_PORT((const struct sockaddr_in6*)(const void*)(sockaddr), ipaddr, port)
+#define DOMAIN_TO_NETCONN_TYPE(domain, netconn_type) (netconn_type)
+#else /*-> LWIP_IPV4: LWIP_IPV4 && LWIP_IPV6 */
+#define IS_SOCK_ADDR_LEN_VALID(namelen)  ((namelen) == sizeof(struct sockaddr_in))
+#define IS_SOCK_ADDR_TYPE_VALID(name)    ((name)->sa_family == AF_INET)
+#define SOCK_ADDR_TYPE_MATCH(name, sock) 1
+#define IPADDR_PORT_TO_SOCKADDR(sockaddr, ipaddr, port) \
+        IP4ADDR_PORT_TO_SOCKADDR((struct sockaddr_in*)(void*)(sockaddr), ip_2_ip4(ipaddr), port)
+#define SOCKADDR_TO_IPADDR_PORT(sockaddr, ipaddr, port) \
+        SOCKADDR4_TO_IP4ADDR_PORT((const struct sockaddr_in*)(const void*)(sockaddr), ipaddr, port)
+#define DOMAIN_TO_NETCONN_TYPE(domain, netconn_type) (netconn_type)
+#endif /* LWIP_IPV6 */
+
+#define IS_SOCK_ADDR_TYPE_VALID_OR_UNSPEC(name)    (((name)->sa_family == AF_UNSPEC) || \
+                                                    IS_SOCK_ADDR_TYPE_VALID(name))
+#define SOCK_ADDR_TYPE_MATCH_OR_UNSPEC(name, sock) (((name)->sa_family == AF_UNSPEC) || \
+                                                    SOCK_ADDR_TYPE_MATCH(name, sock))
+#define IS_SOCK_ADDR_ALIGNED(name)      ((((mem_ptr_t)(name)) % 4) == 0)
+
+
+#define LWIP_SOCKOPT_CHECK_OPTLEN(optlen, opttype) do { if ((optlen) < sizeof(opttype)) { return EINVAL; }}while(0)
+#define LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, opttype) do { \
+  LWIP_SOCKOPT_CHECK_OPTLEN(optlen, opttype); \
+  if ((sock)->conn == NULL) { return EINVAL; } }while(0)
+#define LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, opttype) do { \
+  LWIP_SOCKOPT_CHECK_OPTLEN(optlen, opttype); \
+  if (((sock)->conn == NULL) || ((sock)->conn->pcb.tcp == NULL)) { return EINVAL; } }while(0)
+#define LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, opttype, netconntype) do { \
+  LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, opttype); \
+  if (NETCONNTYPE_GROUP(netconn_type((sock)->conn)) != netconntype) { return ENOPROTOOPT; } }while(0)
+
+
+#define LWIP_SETGETSOCKOPT_DATA_VAR_REF(name)     API_VAR_REF(name)
+#define LWIP_SETGETSOCKOPT_DATA_VAR_DECLARE(name) API_VAR_DECLARE(struct lwip_setgetsockopt_data, name)
+#define LWIP_SETGETSOCKOPT_DATA_VAR_FREE(name)    API_VAR_FREE(MEMP_SOCKET_SETGETSOCKOPT_DATA, name)
+#if LWIP_MPU_COMPATIBLE
+#define LWIP_SETGETSOCKOPT_DATA_VAR_ALLOC(name, sock) do { \
+  name = (struct lwip_setgetsockopt_data *)memp_malloc(MEMP_SOCKET_SETGETSOCKOPT_DATA); \
+  if (name == NULL) { \
+    sock_set_errno(sock, ENOMEM); \
+    return -1; \
+  } }while(0)
+#else /* LWIP_MPU_COMPATIBLE */
+#define LWIP_SETGETSOCKOPT_DATA_VAR_ALLOC(name, sock)
+#endif /* LWIP_MPU_COMPATIBLE */
+
+#if LWIP_SO_SNDRCVTIMEO_NONSTANDARD
+#define LWIP_SO_SNDRCVTIMEO_OPTTYPE int
+#define LWIP_SO_SNDRCVTIMEO_SET(optval, val) (*(int *)(optval) = (val))
+#define LWIP_SO_SNDRCVTIMEO_GET_MS(optval)   ((s32_t)*(const int*)(optval))
+#else
+#define LWIP_SO_SNDRCVTIMEO_OPTTYPE struct timeval
+#define LWIP_SO_SNDRCVTIMEO_SET(optval, val)  do { \
+  s32_t loc = (val); \
+  ((struct timeval *)(optval))->tv_sec = (loc) / 1000U; \
+  ((struct timeval *)(optval))->tv_usec = ((loc) % 1000U) * 1000U; }while(0)
+#define LWIP_SO_SNDRCVTIMEO_GET_MS(optval) ((((const struct timeval *)(optval))->tv_sec * 1000U) + (((const struct timeval *)(optval))->tv_usec / 1000U))
+#endif
+
+#define NUM_SOCKETS MEMP_NUM_NETCONN
+
+/** This is overridable for the rare case where more than 255 threads
+ * select on the same socket...
+ */
+#ifndef SELWAIT_T
+#define SELWAIT_T u8_t
+#endif
+
+/** Contains all internal pointers and states used for a socket */
+struct lwip_sock {
+  /** sockets currently are built on netconns, each socket has one netconn */
+  struct netconn *conn;
+  /** data that was left from the previous read */
+  void *lastdata;
+  /** offset in the data that was left from the previous read */
+  u16_t lastoffset;
+  /** number of times data was received, set by event_callback(),
+      tested by the receive and select functions */
+  s16_t rcvevent;
+  /** number of times data was ACKed (free send buffer), set by event_callback(),
+      tested by select */
+  u16_t sendevent;
+  /** error happened for this socket, set by event_callback(), tested by select */
+  u16_t errevent;
+  /** last error that occurred on this socket (in fact, all our errnos fit into an u8_t) */
+  u8_t err;
+  /** counter of how many threads are waiting for this socket using select */
+  SELWAIT_T select_waiting;
+};
+
+#if LWIP_NETCONN_SEM_PER_THREAD
+#define SELECT_SEM_T        sys_sem_t*
+#define SELECT_SEM_PTR(sem) (sem)
+#else /* LWIP_NETCONN_SEM_PER_THREAD */
+#define SELECT_SEM_T        sys_sem_t
+#define SELECT_SEM_PTR(sem) (&(sem))
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+/** Description for a task waiting in select */
+struct lwip_select_cb {
+  /** Pointer to the next waiting task */
+  struct lwip_select_cb *next;
+  /** Pointer to the previous waiting task */
+  struct lwip_select_cb *prev;
+  /** readset passed to select */
+  fd_set *readset;
+  /** writeset passed to select */
+  fd_set *writeset;
+  /** unimplemented: exceptset passed to select */
+  fd_set *exceptset;
+  /** don't signal the same semaphore twice: set to 1 when signalled */
+  int sem_signalled;
+  /** semaphore to wake up a task waiting for select */
+  SELECT_SEM_T sem;
+};
+
+/** A struct sockaddr replacement that has the same alignment as sockaddr_in/
+ *  sockaddr_in6 if instantiated.
+ */
+union sockaddr_aligned {
+   struct sockaddr sa;
+#if LWIP_IPV6
+   struct sockaddr_in6 sin6;
+#endif /* LWIP_IPV6 */
+#if LWIP_IPV4
+   struct sockaddr_in sin;
+#endif /* LWIP_IPV4 */
+};
+
+#if LWIP_IGMP
+/* Define the number of IPv4 multicast memberships, default is one per socket */
+#ifndef LWIP_SOCKET_MAX_MEMBERSHIPS
+#define LWIP_SOCKET_MAX_MEMBERSHIPS NUM_SOCKETS
+#endif
+
+/* This is to keep track of IP_ADD_MEMBERSHIP calls to drop the membership when
+   a socket is closed */
+struct lwip_socket_multicast_pair {
+  /** the socket */
+  struct lwip_sock* sock;
+  /** the interface address */
+  ip4_addr_t if_addr;
+  /** the group address */
+  ip4_addr_t multi_addr;
+};
+
+struct lwip_socket_multicast_pair socket_ipv4_multicast_memberships[LWIP_SOCKET_MAX_MEMBERSHIPS];
+
+static int  lwip_socket_register_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr);
+static void lwip_socket_unregister_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr);
+static void lwip_socket_drop_registered_memberships(int s);
+#endif /* LWIP_IGMP */
+
+/** The global array of available sockets */
+static struct lwip_sock sockets[NUM_SOCKETS];
+/** The global list of tasks waiting for select */
+static struct lwip_select_cb *select_cb_list;
+/** This counter is increased from lwip_select when the list is changed
+    and checked in event_callback to see if it has changed. */
+static volatile int select_cb_ctr;
+
+#if LWIP_SOCKET_SET_ERRNO
+#ifndef set_errno
+#define set_errno(err) do { if (err) { errno = (err); } } while(0)
+#endif
+#else /* LWIP_SOCKET_SET_ERRNO */
+#define set_errno(err)
+#endif /* LWIP_SOCKET_SET_ERRNO */
+
+#define sock_set_errno(sk, e) do { \
+  const int sockerr = (e); \
+  sk->err = (u8_t)sockerr; \
+  set_errno(sockerr); \
+} while (0)
+
+/* Forward declaration of some functions */
+static void event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len);
+#if !LWIP_TCPIP_CORE_LOCKING
+static void lwip_getsockopt_callback(void *arg);
+static void lwip_setsockopt_callback(void *arg);
+#endif
+static u8_t lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *optlen);
+static u8_t lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_t optlen);
+
+#if LWIP_IPV4 && LWIP_IPV6
+static void
+sockaddr_to_ipaddr_port(const struct sockaddr* sockaddr, ip_addr_t* ipaddr, u16_t* port)
+{
+  if ((sockaddr->sa_family) == AF_INET6) {
+    SOCKADDR6_TO_IP6ADDR_PORT((const struct sockaddr_in6*)(const void*)(sockaddr), ipaddr, *port);
+    ipaddr->type = IPADDR_TYPE_V6;
+  } else {
+    SOCKADDR4_TO_IP4ADDR_PORT((const struct sockaddr_in*)(const void*)(sockaddr), ipaddr, *port);
+    ipaddr->type = IPADDR_TYPE_V4;
+  }
+}
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+/** LWIP_NETCONN_SEM_PER_THREAD==1: initialize thread-local semaphore */
+void
+lwip_socket_thread_init(void)
+{
+   netconn_thread_init();
+}
+
+/** LWIP_NETCONN_SEM_PER_THREAD==1: destroy thread-local semaphore */
+void
+lwip_socket_thread_cleanup(void)
+{
+   netconn_thread_cleanup();
+}
+
+/**
+ * Map a externally used socket index to the internal socket representation.
+ *
+ * @param s externally used socket index
+ * @return struct lwip_sock for the socket or NULL if not found
+ */
+static struct lwip_sock *
+get_socket(int s)
+{
+  struct lwip_sock *sock;
+
+  s -= LWIP_SOCKET_OFFSET;
+
+  if ((s < 0) || (s >= NUM_SOCKETS)) {
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("get_socket(%d): invalid\n", s + LWIP_SOCKET_OFFSET));
+    set_errno(EBADF);
+    return NULL;
+  }
+
+  sock = &sockets[s];
+
+  if (!sock->conn) {
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("get_socket(%d): not active\n", s + LWIP_SOCKET_OFFSET));
+    set_errno(EBADF);
+    return NULL;
+  }
+
+  return sock;
+}
+
+/**
+ * Same as get_socket but doesn't set errno
+ *
+ * @param s externally used socket index
+ * @return struct lwip_sock for the socket or NULL if not found
+ */
+static struct lwip_sock *
+tryget_socket(int s)
+{
+  s -= LWIP_SOCKET_OFFSET;
+  if ((s < 0) || (s >= NUM_SOCKETS)) {
+    return NULL;
+  }
+  if (!sockets[s].conn) {
+    return NULL;
+  }
+  return &sockets[s];
+}
+
+/**
+ * Allocate a new socket for a given netconn.
+ *
+ * @param newconn the netconn for which to allocate a socket
+ * @param accepted 1 if socket has been created by accept(),
+ *                 0 if socket has been created by socket()
+ * @return the index of the new socket; -1 on error
+ */
+static int
+alloc_socket(struct netconn *newconn, int accepted)
+{
+  int i;
+  SYS_ARCH_DECL_PROTECT(lev);
+
+  /* allocate a new socket identifier */
+  for (i = 0; i < NUM_SOCKETS; ++i) {
+    /* Protect socket array */
+    SYS_ARCH_PROTECT(lev);
+    if (!sockets[i].conn && (sockets[i].select_waiting == 0)) {
+      sockets[i].conn       = newconn;
+      /* The socket is not yet known to anyone, so no need to protect
+         after having marked it as used. */
+      SYS_ARCH_UNPROTECT(lev);
+      sockets[i].lastdata   = NULL;
+      sockets[i].lastoffset = 0;
+      sockets[i].rcvevent   = 0;
+      /* TCP sendbuf is empty, but the socket is not yet writable until connected
+       * (unless it has been created by accept()). */
+      sockets[i].sendevent  = (NETCONNTYPE_GROUP(newconn->type) == NETCONN_TCP ? (accepted != 0) : 1);
+      sockets[i].errevent   = 0;
+      sockets[i].err        = 0;
+      return i + LWIP_SOCKET_OFFSET;
+    }
+    SYS_ARCH_UNPROTECT(lev);
+  }
+  return -1;
+}
+
+/** Free a socket. The socket's netconn must have been
+ * delete before!
+ *
+ * @param sock the socket to free
+ * @param is_tcp != 0 for TCP sockets, used to free lastdata
+ */
+static void
+free_socket(struct lwip_sock *sock, int is_tcp)
+{
+  void *lastdata;
+
+  lastdata         = sock->lastdata;
+  sock->lastdata   = NULL;
+  sock->lastoffset = 0;
+  sock->err        = 0;
+
+  /* Protect socket array */
+  SYS_ARCH_SET(sock->conn, NULL);
+  /* don't use 'sock' after this line, as another task might have allocated it */
+
+  if (lastdata != NULL) {
+    if (is_tcp) {
+      pbuf_free((struct pbuf *)lastdata);
+    } else {
+      netbuf_delete((struct netbuf *)lastdata);
+    }
+  }
+}
+
+/* Below this, the well-known socket functions are implemented.
+ * Use google.com or opengroup.org to get a good description :-)
+ *
+ * Exceptions are documented!
+ */
+
+int
+lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen)
+{
+  struct lwip_sock *sock, *nsock;
+  struct netconn *newconn;
+  ip_addr_t naddr;
+  u16_t port = 0;
+  int newsock;
+  err_t err;
+  SYS_ARCH_DECL_PROTECT(lev);
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d)...\n", s));
+  sock = get_socket(s);
+  if (!sock) {
+    return -1;
+  }
+
+  if (netconn_is_nonblocking(sock->conn) && (sock->rcvevent <= 0)) {
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): returning EWOULDBLOCK\n", s));
+    set_errno(EWOULDBLOCK);
+    return -1;
+  }
+
+  /* wait for a new connection */
+  err = netconn_accept(sock->conn, &newconn);
+  if (err != ERR_OK) {
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): netconn_acept failed, err=%d\n", s, err));
+    if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
+      sock_set_errno(sock, EOPNOTSUPP);
+    } else if (err == ERR_CLSD) {
+      sock_set_errno(sock, EINVAL);
+    } else {
+      sock_set_errno(sock, err_to_errno(err));
+    }
+    return -1;
+  }
+  LWIP_ASSERT("newconn != NULL", newconn != NULL);
+
+  newsock = alloc_socket(newconn, 1);
+  if (newsock == -1) {
+    netconn_delete(newconn);
+    sock_set_errno(sock, ENFILE);
+    return -1;
+  }
+  LWIP_ASSERT("invalid socket index", (newsock >= LWIP_SOCKET_OFFSET) && (newsock < NUM_SOCKETS + LWIP_SOCKET_OFFSET));
+  LWIP_ASSERT("newconn->callback == event_callback", newconn->callback == event_callback);
+  nsock = &sockets[newsock - LWIP_SOCKET_OFFSET];
+
+  /* See event_callback: If data comes in right away after an accept, even
+   * though the server task might not have created a new socket yet.
+   * In that case, newconn->socket is counted down (newconn->socket--),
+   * so nsock->rcvevent is >= 1 here!
+   */
+  SYS_ARCH_PROTECT(lev);
+  nsock->rcvevent += (s16_t)(-1 - newconn->socket);
+  newconn->socket = newsock;
+  SYS_ARCH_UNPROTECT(lev);
+
+  /* Note that POSIX only requires us to check addr is non-NULL. addrlen must
+   * not be NULL if addr is valid.
+   */
+  if (addr != NULL) {
+    union sockaddr_aligned tempaddr;
+    /* get the IP address and port of the remote host */
+    err = netconn_peer(newconn, &naddr, &port);
+    if (err != ERR_OK) {
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): netconn_peer failed, err=%d\n", s, err));
+      netconn_delete(newconn);
+      free_socket(nsock, 1);
+      sock_set_errno(sock, err_to_errno(err));
+      return -1;
+    }
+    LWIP_ASSERT("addr valid but addrlen NULL", addrlen != NULL);
+
+    IPADDR_PORT_TO_SOCKADDR(&tempaddr, &naddr, port);
+    if (*addrlen > tempaddr.sa.sa_len) {
+      *addrlen = tempaddr.sa.sa_len;
+    }
+    MEMCPY(addr, &tempaddr, *addrlen);
+
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d) returning new sock=%d addr=", s, newsock));
+    ip_addr_debug_print_val(SOCKETS_DEBUG, naddr);
+    LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F"\n", port));
+  } else {
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d) returning new sock=%d", s, newsock));
+  }
+
+  sock_set_errno(sock, 0);
+  return newsock;
+}
+
+int
+lwip_bind(int s, const struct sockaddr *name, socklen_t namelen)
+{
+  struct lwip_sock *sock;
+  ip_addr_t local_addr;
+  u16_t local_port;
+  err_t err;
+
+  sock = get_socket(s);
+  if (!sock) {
+    return -1;
+  }
+
+  if (!SOCK_ADDR_TYPE_MATCH(name, sock)) {
+    /* sockaddr does not match socket type (IPv4/IPv6) */
+    sock_set_errno(sock, err_to_errno(ERR_VAL));
+    return -1;
+  }
+
+  /* check size, family and alignment of 'name' */
+  LWIP_ERROR("lwip_bind: invalid address", (IS_SOCK_ADDR_LEN_VALID(namelen) &&
+             IS_SOCK_ADDR_TYPE_VALID(name) && IS_SOCK_ADDR_ALIGNED(name)),
+             sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
+  LWIP_UNUSED_ARG(namelen);
+
+  SOCKADDR_TO_IPADDR_PORT(name, &local_addr, local_port);
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d, addr=", s));
+  ip_addr_debug_print_val(SOCKETS_DEBUG, local_addr);
+  LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", local_port));
+
+#if LWIP_IPV4 && LWIP_IPV6
+  /* Dual-stack: Unmap IPv4 mapped IPv6 addresses */
+  if (IP_IS_V6_VAL(local_addr) && ip6_addr_isipv4mappedipv6(ip_2_ip6(&local_addr))) {
+    unmap_ipv4_mapped_ipv6(ip_2_ip4(&local_addr), ip_2_ip6(&local_addr));
+    IP_SET_TYPE_VAL(local_addr, IPADDR_TYPE_V4);
+  }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+  err = netconn_bind(sock->conn, &local_addr, local_port);
+
+  if (err != ERR_OK) {
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d) failed, err=%d\n", s, err));
+    sock_set_errno(sock, err_to_errno(err));
+    return -1;
+  }
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d) succeeded\n", s));
+  sock_set_errno(sock, 0);
+  return 0;
+}
+
+int
+lwip_close(int s)
+{
+  struct lwip_sock *sock;
+  int is_tcp = 0;
+  err_t err;
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_close(%d)\n", s));
+
+  sock = get_socket(s);
+  if (!sock) {
+    return -1;
+  }
+
+  if (sock->conn != NULL) {
+    is_tcp = NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP;
+  } else {
+    LWIP_ASSERT("sock->lastdata == NULL", sock->lastdata == NULL);
+  }
+
+#if LWIP_IGMP
+  /* drop all possibly joined IGMP memberships */
+  lwip_socket_drop_registered_memberships(s);
+#endif /* LWIP_IGMP */
+
+  err = netconn_delete(sock->conn);
+  if (err != ERR_OK) {
+    sock_set_errno(sock, err_to_errno(err));
+    return -1;
+  }
+
+  free_socket(sock, is_tcp);
+  set_errno(0);
+  return 0;
+}
+
+int
+lwip_connect(int s, const struct sockaddr *name, socklen_t namelen)
+{
+  struct lwip_sock *sock;
+  err_t err;
+
+  sock = get_socket(s);
+  if (!sock) {
+    return -1;
+  }
+
+  if (!SOCK_ADDR_TYPE_MATCH_OR_UNSPEC(name, sock)) {
+    /* sockaddr does not match socket type (IPv4/IPv6) */
+    sock_set_errno(sock, err_to_errno(ERR_VAL));
+    return -1;
+  }
+
+  LWIP_UNUSED_ARG(namelen);
+  if (name->sa_family == AF_UNSPEC) {
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, AF_UNSPEC)\n", s));
+    err = netconn_disconnect(sock->conn);
+  } else {
+    ip_addr_t remote_addr;
+    u16_t remote_port;
+
+    /* check size, family and alignment of 'name' */
+    LWIP_ERROR("lwip_connect: invalid address", IS_SOCK_ADDR_LEN_VALID(namelen) &&
+               IS_SOCK_ADDR_TYPE_VALID_OR_UNSPEC(name) && IS_SOCK_ADDR_ALIGNED(name),
+               sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
+
+    SOCKADDR_TO_IPADDR_PORT(name, &remote_addr, remote_port);
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, addr=", s));
+    ip_addr_debug_print_val(SOCKETS_DEBUG, remote_addr);
+    LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", remote_port));
+
+#if LWIP_IPV4 && LWIP_IPV6
+    /* Dual-stack: Unmap IPv4 mapped IPv6 addresses */
+    if (IP_IS_V6_VAL(remote_addr) && ip6_addr_isipv4mappedipv6(ip_2_ip6(&remote_addr))) {
+      unmap_ipv4_mapped_ipv6(ip_2_ip4(&remote_addr), ip_2_ip6(&remote_addr));
+      IP_SET_TYPE_VAL(remote_addr, IPADDR_TYPE_V4);
+    }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+    err = netconn_connect(sock->conn, &remote_addr, remote_port);
+  }
+
+  if (err != ERR_OK) {
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d) failed, err=%d\n", s, err));
+    sock_set_errno(sock, err_to_errno(err));
+    return -1;
+  }
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d) succeeded\n", s));
+  sock_set_errno(sock, 0);
+  return 0;
+}
+
+/**
+ * Set a socket into listen mode.
+ * The socket may not have been used for another connection previously.
+ *
+ * @param s the socket to set to listening mode
+ * @param backlog (ATTENTION: needs TCP_LISTEN_BACKLOG=1)
+ * @return 0 on success, non-zero on failure
+ */
+int
+lwip_listen(int s, int backlog)
+{
+  struct lwip_sock *sock;
+  err_t err;
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_listen(%d, backlog=%d)\n", s, backlog));
+
+  sock = get_socket(s);
+  if (!sock) {
+    return -1;
+  }
+
+  /* limit the "backlog" parameter to fit in an u8_t */
+  backlog = LWIP_MIN(LWIP_MAX(backlog, 0), 0xff);
+
+  err = netconn_listen_with_backlog(sock->conn, (u8_t)backlog);
+
+  if (err != ERR_OK) {
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_listen(%d) failed, err=%d\n", s, err));
+    if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
+      sock_set_errno(sock, EOPNOTSUPP);
+      return -1;
+    }
+    sock_set_errno(sock, err_to_errno(err));
+    return -1;
+  }
+
+  sock_set_errno(sock, 0);
+  return 0;
+}
+
+int
+lwip_recvfrom(int s, void *mem, size_t len, int flags,
+              struct sockaddr *from, socklen_t *fromlen)
+{
+  struct lwip_sock *sock;
+  void             *buf = NULL;
+  struct pbuf      *p;
+  u16_t            buflen, copylen;
+  int              off = 0;
+  u8_t             done = 0;
+  err_t            err;
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d, %p, %"SZT_F", 0x%x, ..)\n", s, mem, len, flags));
+  sock = get_socket(s);
+  if (!sock) {
+    return -1;
+  }
+
+  do {
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: top while sock->lastdata=%p\n", sock->lastdata));
+    /* Check if there is data left from the last recv operation. */
+    if (sock->lastdata) {
+      buf = sock->lastdata;
+    } else {
+      /* If this is non-blocking call, then check first */
+      if (((flags & MSG_DONTWAIT) || netconn_is_nonblocking(sock->conn)) &&
+          (sock->rcvevent <= 0)) {
+        if (off > 0) {
+          /* already received data, return that */
+          sock_set_errno(sock, 0);
+          return off;
+        }
+        LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): returning EWOULDBLOCK\n", s));
+        set_errno(EWOULDBLOCK);
+        return -1;
+      }
+
+      /* No data was left from the previous operation, so we try to get
+         some from the network. */
+      if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
+        err = netconn_recv_tcp_pbuf(sock->conn, (struct pbuf **)&buf);
+      } else {
+        err = netconn_recv(sock->conn, (struct netbuf **)&buf);
+      }
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: netconn_recv err=%d, netbuf=%p\n",
+        err, buf));
+
+      if (err != ERR_OK) {
+        if (off > 0) {
+          if (err == ERR_CLSD) {
+            /* closed but already received data, ensure select gets the FIN, too */
+            event_callback(sock->conn, NETCONN_EVT_RCVPLUS, 0);
+          }
+          /* already received data, return that */
+          sock_set_errno(sock, 0);
+          return off;
+        }
+        /* We should really do some error checking here. */
+        LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): buf == NULL, error is \"%s\"!\n",
+          s, lwip_strerr(err)));
+        sock_set_errno(sock, err_to_errno(err));
+        if (err == ERR_CLSD) {
+          return 0;
+        } else {
+          return -1;
+        }
+      }
+      LWIP_ASSERT("buf != NULL", buf != NULL);
+      sock->lastdata = buf;
+    }
+
+    if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
+      p = (struct pbuf *)buf;
+    } else {
+      p = ((struct netbuf *)buf)->p;
+    }
+    buflen = p->tot_len;
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: buflen=%"U16_F" len=%"SZT_F" off=%d sock->lastoffset=%"U16_F"\n",
+      buflen, len, off, sock->lastoffset));
+
+    buflen -= sock->lastoffset;
+
+    if (len > buflen) {
+      copylen = buflen;
+    } else {
+      copylen = (u16_t)len;
+    }
+
+    /* copy the contents of the received buffer into
+    the supplied memory pointer mem */
+    pbuf_copy_partial(p, (u8_t*)mem + off, copylen, sock->lastoffset);
+
+    off += copylen;
+
+    if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
+      LWIP_ASSERT("invalid copylen, len would underflow", len >= copylen);
+      len -= copylen;
+      if ((len <= 0) ||
+          (p->flags & PBUF_FLAG_PUSH) ||
+          (sock->rcvevent <= 0) ||
+          ((flags & MSG_PEEK) != 0)) {
+        done = 1;
+      }
+    } else {
+      done = 1;
+    }
+
+    /* Check to see from where the data was.*/
+    if (done) {
+#if !SOCKETS_DEBUG
+      if (from && fromlen)
+#endif /* !SOCKETS_DEBUG */
+      {
+        u16_t port;
+        ip_addr_t tmpaddr;
+        ip_addr_t *fromaddr;
+        union sockaddr_aligned saddr;
+        LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): addr=", s));
+        if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
+          fromaddr = &tmpaddr;
+          netconn_getaddr(sock->conn, fromaddr, &port, 0);
+        } else {
+          port = netbuf_fromport((struct netbuf *)buf);
+          fromaddr = netbuf_fromaddr((struct netbuf *)buf);
+        }
+
+#if LWIP_IPV4 && LWIP_IPV6
+        /* Dual-stack: Map IPv4 addresses to IPv4 mapped IPv6 */
+        if (NETCONNTYPE_ISIPV6(netconn_type(sock->conn)) && IP_IS_V4(fromaddr)) {
+          ip4_2_ipv4_mapped_ipv6(ip_2_ip6(fromaddr), ip_2_ip4(fromaddr));
+          IP_SET_TYPE(fromaddr, IPADDR_TYPE_V6);
+        }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+        IPADDR_PORT_TO_SOCKADDR(&saddr, fromaddr, port);
+        ip_addr_debug_print(SOCKETS_DEBUG, fromaddr);
+        LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F" len=%d\n", port, off));
+#if SOCKETS_DEBUG
+        if (from && fromlen)
+#endif /* SOCKETS_DEBUG */
+        {
+          if (*fromlen > saddr.sa.sa_len) {
+            *fromlen = saddr.sa.sa_len;
+          }
+          MEMCPY(from, &saddr, *fromlen);
+        }
+      }
+    }
+
+    /* If we don't peek the incoming message... */
+    if ((flags & MSG_PEEK) == 0) {
+      /* If this is a TCP socket, check if there is data left in the
+         buffer. If so, it should be saved in the sock structure for next
+         time around. */
+      if ((NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) && (buflen - copylen > 0)) {
+        sock->lastdata = buf;
+        sock->lastoffset += copylen;
+        LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: lastdata now netbuf=%p\n", buf));
+      } else {
+        sock->lastdata = NULL;
+        sock->lastoffset = 0;
+        LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: deleting netbuf=%p\n", buf));
+        if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
+          pbuf_free((struct pbuf *)buf);
+        } else {
+          netbuf_delete((struct netbuf *)buf);
+        }
+        buf = NULL;
+      }
+    }
+  } while (!done);
+
+  sock_set_errno(sock, 0);
+  return off;
+}
+
+int
+lwip_read(int s, void *mem, size_t len)
+{
+  return lwip_recvfrom(s, mem, len, 0, NULL, NULL);
+}
+
+int
+lwip_recv(int s, void *mem, size_t len, int flags)
+{
+  return lwip_recvfrom(s, mem, len, flags, NULL, NULL);
+}
+
+int
+lwip_send(int s, const void *data, size_t size, int flags)
+{
+  struct lwip_sock *sock;
+  err_t err;
+  u8_t write_flags;
+  size_t written;
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d, data=%p, size=%"SZT_F", flags=0x%x)\n",
+                              s, data, size, flags));
+
+  sock = get_socket(s);
+  if (!sock) {
+    return -1;
+  }
+
+  if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
+#if (LWIP_UDP || LWIP_RAW)
+    return lwip_sendto(s, data, size, flags, NULL, 0);
+#else /* (LWIP_UDP || LWIP_RAW) */
+    sock_set_errno(sock, err_to_errno(ERR_ARG));
+    return -1;
+#endif /* (LWIP_UDP || LWIP_RAW) */
+  }
+
+  write_flags = NETCONN_COPY |
+    ((flags & MSG_MORE)     ? NETCONN_MORE      : 0) |
+    ((flags & MSG_DONTWAIT) ? NETCONN_DONTBLOCK : 0);
+  written = 0;
+  err = netconn_write_partly(sock->conn, data, size, write_flags, &written);
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d) err=%d written=%"SZT_F"\n", s, err, written));
+  sock_set_errno(sock, err_to_errno(err));
+  return (err == ERR_OK ? (int)written : -1);
+}
+
+int
+lwip_sendmsg(int s, const struct msghdr *msg, int flags)
+{
+  struct lwip_sock *sock;
+  int i;
+#if LWIP_TCP
+  u8_t write_flags;
+  size_t written;
+#endif
+  int size = 0;
+  err_t err = ERR_OK;
+
+  sock = get_socket(s);
+  if (!sock) {
+    return -1;
+  }
+
+  LWIP_ERROR("lwip_sendmsg: invalid msghdr", msg != NULL,
+             sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
+
+  LWIP_UNUSED_ARG(msg->msg_control);
+  LWIP_UNUSED_ARG(msg->msg_controllen);
+  LWIP_UNUSED_ARG(msg->msg_flags);
+  LWIP_ERROR("lwip_sendmsg: invalid msghdr iov", (msg->msg_iov != NULL && msg->msg_iovlen != 0),
+             sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
+
+  if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
+#if LWIP_TCP
+    write_flags = NETCONN_COPY |
+    ((flags & MSG_MORE)     ? NETCONN_MORE      : 0) |
+    ((flags & MSG_DONTWAIT) ? NETCONN_DONTBLOCK : 0);
+
+    for (i = 0; i < msg->msg_iovlen; i++) {
+      u8_t apiflags = write_flags;
+      if (i + 1 < msg->msg_iovlen) {
+        apiflags |= NETCONN_MORE;
+      }
+      written = 0;
+      err = netconn_write_partly(sock->conn, msg->msg_iov[i].iov_base, msg->msg_iov[i].iov_len, write_flags, &written);
+      if (err == ERR_OK) {
+        size += written;
+        /* check that the entire IO vector was accepected, if not return a partial write */
+        if (written != msg->msg_iov[i].iov_len)
+          break;
+      }
+      /* none of this IO vector was accepted, but previous was, return partial write and conceal ERR_WOULDBLOCK */
+      else if (err == ERR_WOULDBLOCK && size > 0) {
+        err = ERR_OK;
+        /* let ERR_WOULDBLOCK persist on the netconn since we are returning ERR_OK */
+        break;
+      } else {
+        size = -1;
+        break;
+      }
+    }
+    sock_set_errno(sock, err_to_errno(err));
+    return size;
+#else /* LWIP_TCP */
+    sock_set_errno(sock, err_to_errno(ERR_ARG));
+    return -1;
+#endif /* LWIP_TCP */
+  }
+  /* else, UDP and RAW NETCONNs */
+#if LWIP_UDP || LWIP_RAW
+  {
+    struct netbuf *chain_buf;
+
+    LWIP_UNUSED_ARG(flags);
+    LWIP_ERROR("lwip_sendmsg: invalid msghdr name", (((msg->msg_name == NULL) && (msg->msg_namelen == 0)) ||
+               IS_SOCK_ADDR_LEN_VALID(msg->msg_namelen)) ,
+               sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
+
+    /* initialize chain buffer with destination */
+    chain_buf = netbuf_new();
+    if (!chain_buf) {
+      sock_set_errno(sock, err_to_errno(ERR_MEM));
+      return -1;
+    }
+    if (msg->msg_name) {
+      u16_t remote_port;
+      SOCKADDR_TO_IPADDR_PORT((const struct sockaddr *)msg->msg_name, &chain_buf->addr, remote_port);
+      netbuf_fromport(chain_buf) = remote_port;
+    }
+#if LWIP_NETIF_TX_SINGLE_PBUF
+    for (i = 0; i < msg->msg_iovlen; i++) {
+      size += msg->msg_iov[i].iov_len;
+    }
+    /* Allocate a new netbuf and copy the data into it. */
+    if (netbuf_alloc(chain_buf, (u16_t)size) == NULL) {
+       err = ERR_MEM;
+    } else {
+      /* flatten the IO vectors */
+      size_t offset = 0;
+      for (i = 0; i < msg->msg_iovlen; i++) {
+        MEMCPY(&((u8_t*)chain_buf->p->payload)[offset], msg->msg_iov[i].iov_base, msg->msg_iov[i].iov_len);
+        offset += msg->msg_iov[i].iov_len;
+      }
+#if LWIP_CHECKSUM_ON_COPY
+      {
+        /* This can be improved by using LWIP_CHKSUM_COPY() and aggregating the checksum for each IO vector */
+        u16_t chksum = ~inet_chksum_pbuf(chain_buf->p);
+        netbuf_set_chksum(chain_buf, chksum);
+      }
+#endif /* LWIP_CHECKSUM_ON_COPY */
+      err = ERR_OK;
+    }
+#else /* LWIP_NETIF_TX_SINGLE_PBUF */
+    /* create a chained netbuf from the IO vectors. NOTE: we assemble a pbuf chain
+       manually to avoid having to allocate, chain, and delete a netbuf for each iov */
+    for (i = 0; i < msg->msg_iovlen; i++) {
+      struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_REF);
+      if (p == NULL) {
+        err = ERR_MEM; /* let netbuf_delete() cleanup chain_buf */
+        break;
+      }
+      p->payload = msg->msg_iov[i].iov_base;
+      LWIP_ASSERT("iov_len < u16_t", msg->msg_iov[i].iov_len <= 0xFFFF);
+      p->len = p->tot_len = (u16_t)msg->msg_iov[i].iov_len;
+      /* netbuf empty, add new pbuf */
+      if (chain_buf->p == NULL) {
+        chain_buf->p = chain_buf->ptr = p;
+        /* add pbuf to existing pbuf chain */
+      } else {
+        pbuf_cat(chain_buf->p, p);
+      }
+    }
+    /* save size of total chain */
+    if (err == ERR_OK) {
+      size = netbuf_len(chain_buf);
+    }
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
+
+    if (err == ERR_OK) {
+#if LWIP_IPV4 && LWIP_IPV6
+      /* Dual-stack: Unmap IPv4 mapped IPv6 addresses */
+      if (IP_IS_V6_VAL(chain_buf->addr) && ip6_addr_isipv4mappedipv6(ip_2_ip6(&chain_buf->addr))) {
+        unmap_ipv4_mapped_ipv6(ip_2_ip4(&chain_buf->addr), ip_2_ip6(&chain_buf->addr));
+        IP_SET_TYPE_VAL(chain_buf->addr, IPADDR_TYPE_V4);
+      }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+      /* send the data */
+      err = netconn_send(sock->conn, chain_buf);
+    }
+
+    /* deallocated the buffer */
+    netbuf_delete(chain_buf);
+
+    sock_set_errno(sock, err_to_errno(err));
+    return (err == ERR_OK ? size : -1);
+  }
+#else /* LWIP_UDP || LWIP_RAW */
+  sock_set_errno(sock, err_to_errno(ERR_ARG));
+  return -1;
+#endif /* LWIP_UDP || LWIP_RAW */
+}
+
+int
+lwip_sendto(int s, const void *data, size_t size, int flags,
+       const struct sockaddr *to, socklen_t tolen)
+{
+  struct lwip_sock *sock;
+  err_t err;
+  u16_t short_size;
+  u16_t remote_port;
+  struct netbuf buf;
+
+  sock = get_socket(s);
+  if (!sock) {
+    return -1;
+  }
+
+  if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
+#if LWIP_TCP
+    return lwip_send(s, data, size, flags);
+#else /* LWIP_TCP */
+    LWIP_UNUSED_ARG(flags);
+    sock_set_errno(sock, err_to_errno(ERR_ARG));
+    return -1;
+#endif /* LWIP_TCP */
+  }
+
+  /* @todo: split into multiple sendto's? */
+  LWIP_ASSERT("lwip_sendto: size must fit in u16_t", size <= 0xffff);
+  short_size = (u16_t)size;
+  LWIP_ERROR("lwip_sendto: invalid address", (((to == NULL) && (tolen == 0)) ||
+             (IS_SOCK_ADDR_LEN_VALID(tolen) &&
+             IS_SOCK_ADDR_TYPE_VALID(to) && IS_SOCK_ADDR_ALIGNED(to))),
+             sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
+  LWIP_UNUSED_ARG(tolen);
+
+  /* initialize a buffer */
+  buf.p = buf.ptr = NULL;
+#if LWIP_CHECKSUM_ON_COPY
+  buf.flags = 0;
+#endif /* LWIP_CHECKSUM_ON_COPY */
+  if (to) {
+    SOCKADDR_TO_IPADDR_PORT(to, &buf.addr, remote_port);
+  } else {
+    remote_port = 0;
+    ip_addr_set_any(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)), &buf.addr);
+  }
+  netbuf_fromport(&buf) = remote_port;
+
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_sendto(%d, data=%p, short_size=%"U16_F", flags=0x%x to=",
+              s, data, short_size, flags));
+  ip_addr_debug_print(SOCKETS_DEBUG, &buf.addr);
+  LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F"\n", remote_port));
+
+  /* make the buffer point to the data that should be sent */
+#if LWIP_NETIF_TX_SINGLE_PBUF
+  /* Allocate a new netbuf and copy the data into it. */
+  if (netbuf_alloc(&buf, short_size) == NULL) {
+    err = ERR_MEM;
+  } else {
+#if LWIP_CHECKSUM_ON_COPY
+    if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_RAW) {
+      u16_t chksum = LWIP_CHKSUM_COPY(buf.p->payload, data, short_size);
+      netbuf_set_chksum(&buf, chksum);
+    } else
+#endif /* LWIP_CHECKSUM_ON_COPY */
+    {
+      MEMCPY(buf.p->payload, data, short_size);
+    }
+    err = ERR_OK;
+  }
+#else /* LWIP_NETIF_TX_SINGLE_PBUF */
+  err = netbuf_ref(&buf, data, short_size);
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
+  if (err == ERR_OK) {
+#if LWIP_IPV4 && LWIP_IPV6
+    /* Dual-stack: Unmap IPv4 mapped IPv6 addresses */
+    if (IP_IS_V6_VAL(buf.addr) && ip6_addr_isipv4mappedipv6(ip_2_ip6(&buf.addr))) {
+      unmap_ipv4_mapped_ipv6(ip_2_ip4(&buf.addr), ip_2_ip6(&buf.addr));
+      IP_SET_TYPE_VAL(buf.addr, IPADDR_TYPE_V4);
+    }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+    /* send the data */
+    err = netconn_send(sock->conn, &buf);
+  }
+
+  /* deallocated the buffer */
+  netbuf_free(&buf);
+
+  sock_set_errno(sock, err_to_errno(err));
+  return (err == ERR_OK ? short_size : -1);
+}
+
+int
+lwip_socket(int domain, int type, int protocol)
+{
+  struct netconn *conn;
+  int i;
+
+  LWIP_UNUSED_ARG(domain); /* @todo: check this */
+
+  /* create a netconn */
+  switch (type) {
+  case SOCK_RAW:
+    conn = netconn_new_with_proto_and_callback(DOMAIN_TO_NETCONN_TYPE(domain, NETCONN_RAW),
+                                               (u8_t)protocol, event_callback);
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_RAW, %d) = ",
+                                 domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol));
+    break;
+  case SOCK_DGRAM:
+    conn = netconn_new_with_callback(DOMAIN_TO_NETCONN_TYPE(domain,
+                 ((protocol == IPPROTO_UDPLITE) ? NETCONN_UDPLITE : NETCONN_UDP)) ,
+                 event_callback);
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_DGRAM, %d) = ",
+                                 domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol));
+    break;
+  case SOCK_STREAM:
+    conn = netconn_new_with_callback(DOMAIN_TO_NETCONN_TYPE(domain, NETCONN_TCP), event_callback);
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_STREAM, %d) = ",
+                                 domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol));
+    break;
+  default:
+
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%d, %d/UNKNOWN, %d) = -1\n",
+                                 domain, type, protocol));
+    set_errno(EINVAL);
+    return -1;
+  }
+
+  if (!conn) {
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("-1 / ENOBUFS (could not create netconn)\n"));
+    set_errno(ENOBUFS);
+    return -1;
+  }
+
+  i = alloc_socket(conn, 0);
+
+  if (i == -1) {
+    netconn_delete(conn);
+    set_errno(ENFILE);
+    return -1;
+  }
+  conn->socket = i;
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("%d\n", i));
+  set_errno(0);
+  return i;
+}
+
+int
+lwip_write(int s, const void *data, size_t size)
+{
+  return lwip_send(s, data, size, 0);
+}
+
+int
+lwip_writev(int s, const struct iovec *iov, int iovcnt)
+{
+  struct msghdr msg;
+
+  msg.msg_name = NULL;
+  msg.msg_namelen = 0;
+  /* Hack: we have to cast via number to cast from 'const' pointer to non-const.
+     Blame the opengroup standard for this inconsistency. */
+  msg.msg_iov = LWIP_CONST_CAST(struct iovec *, iov);
+  msg.msg_iovlen = iovcnt;
+  msg.msg_control = NULL;
+  msg.msg_controllen = 0;
+  msg.msg_flags = 0;
+  return lwip_sendmsg(s, &msg, 0);
+}
+
+/**
+ * Go through the readset and writeset lists and see which socket of the sockets
+ * set in the sets has events. On return, readset, writeset and exceptset have
+ * the sockets enabled that had events.
+ *
+ * @param maxfdp1 the highest socket index in the sets
+ * @param readset_in    set of sockets to check for read events
+ * @param writeset_in   set of sockets to check for write events
+ * @param exceptset_in  set of sockets to check for error events
+ * @param readset_out   set of sockets that had read events
+ * @param writeset_out  set of sockets that had write events
+ * @param exceptset_out set os sockets that had error events
+ * @return number of sockets that had events (read/write/exception) (>= 0)
+ */
+static int
+lwip_selscan(int maxfdp1, fd_set *readset_in, fd_set *writeset_in, fd_set *exceptset_in,
+             fd_set *readset_out, fd_set *writeset_out, fd_set *exceptset_out)
+{
+  int i, nready = 0;
+  fd_set lreadset, lwriteset, lexceptset;
+  struct lwip_sock *sock;
+  SYS_ARCH_DECL_PROTECT(lev);
+
+  FD_ZERO(&lreadset);
+  FD_ZERO(&lwriteset);
+  FD_ZERO(&lexceptset);
+
+  /* Go through each socket in each list to count number of sockets which
+     currently match */
+  for (i = LWIP_SOCKET_OFFSET; i < maxfdp1; i++) {
+    /* if this FD is not in the set, continue */
+    if (!(readset_in && FD_ISSET(i, readset_in)) &&
+        !(writeset_in && FD_ISSET(i, writeset_in)) &&
+        !(exceptset_in && FD_ISSET(i, exceptset_in))) {
+      continue;
+    }
+    /* First get the socket's status (protected)... */
+    SYS_ARCH_PROTECT(lev);
+    sock = tryget_socket(i);
+    if (sock != NULL) {
+      void* lastdata = sock->lastdata;
+      s16_t rcvevent = sock->rcvevent;
+      u16_t sendevent = sock->sendevent;
+      u16_t errevent = sock->errevent;
+      SYS_ARCH_UNPROTECT(lev);
+
+      /* ... then examine it: */
+      /* See if netconn of this socket is ready for read */
+      if (readset_in && FD_ISSET(i, readset_in) && ((lastdata != NULL) || (rcvevent > 0))) {
+        FD_SET(i, &lreadset);
+        LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for reading\n", i));
+        nready++;
+      }
+      /* See if netconn of this socket is ready for write */
+      if (writeset_in && FD_ISSET(i, writeset_in) && (sendevent != 0)) {
+        FD_SET(i, &lwriteset);
+        LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for writing\n", i));
+        nready++;
+      }
+      /* See if netconn of this socket had an error */
+      if (exceptset_in && FD_ISSET(i, exceptset_in) && (errevent != 0)) {
+        FD_SET(i, &lexceptset);
+        LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for exception\n", i));
+        nready++;
+      }
+    } else {
+      SYS_ARCH_UNPROTECT(lev);
+      /* continue on to next FD in list */
+    }
+  }
+  /* copy local sets to the ones provided as arguments */
+  *readset_out = lreadset;
+  *writeset_out = lwriteset;
+  *exceptset_out = lexceptset;
+
+  LWIP_ASSERT("nready >= 0", nready >= 0);
+  return nready;
+}
+
+int
+lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
+            struct timeval *timeout)
+{
+  u32_t waitres = 0;
+  int nready;
+  fd_set lreadset, lwriteset, lexceptset;
+  u32_t msectimeout;
+  struct lwip_select_cb select_cb;
+  int i;
+  int maxfdp2;
+#if LWIP_NETCONN_SEM_PER_THREAD
+  int waited = 0;
+#endif
+  SYS_ARCH_DECL_PROTECT(lev);
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select(%d, %p, %p, %p, tvsec=%"S32_F" tvusec=%"S32_F")\n",
+                  maxfdp1, (void *)readset, (void *) writeset, (void *) exceptset,
+                  timeout ? (s32_t)timeout->tv_sec : (s32_t)-1,
+                  timeout ? (s32_t)timeout->tv_usec : (s32_t)-1));
+
+  /* Go through each socket in each list to count number of sockets which
+     currently match */
+  nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset);
+
+  /* If we don't have any current events, then suspend if we are supposed to */
+  if (!nready) {
+    if (timeout && timeout->tv_sec == 0 && timeout->tv_usec == 0) {
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: no timeout, returning 0\n"));
+      /* This is OK as the local fdsets are empty and nready is zero,
+         or we would have returned earlier. */
+      goto return_copy_fdsets;
+    }
+
+    /* None ready: add our semaphore to list:
+       We don't actually need any dynamic memory. Our entry on the
+       list is only valid while we are in this function, so it's ok
+       to use local variables. */
+
+    select_cb.next = NULL;
+    select_cb.prev = NULL;
+    select_cb.readset = readset;
+    select_cb.writeset = writeset;
+    select_cb.exceptset = exceptset;
+    select_cb.sem_signalled = 0;
+#if LWIP_NETCONN_SEM_PER_THREAD
+    select_cb.sem = LWIP_NETCONN_THREAD_SEM_GET();
+#else /* LWIP_NETCONN_SEM_PER_THREAD */
+    if (sys_sem_new(&select_cb.sem, 0) != ERR_OK) {
+      /* failed to create semaphore */
+      set_errno(ENOMEM);
+      return -1;
+    }
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+    /* Protect the select_cb_list */
+    SYS_ARCH_PROTECT(lev);
+
+    /* Put this select_cb on top of list */
+    select_cb.next = select_cb_list;
+    if (select_cb_list != NULL) {
+      select_cb_list->prev = &select_cb;
+    }
+    select_cb_list = &select_cb;
+    /* Increasing this counter tells event_callback that the list has changed. */
+    select_cb_ctr++;
+
+    /* Now we can safely unprotect */
+    SYS_ARCH_UNPROTECT(lev);
+
+    /* Increase select_waiting for each socket we are interested in */
+    maxfdp2 = maxfdp1;
+    for (i = LWIP_SOCKET_OFFSET; i < maxfdp1; i++) {
+      if ((readset && FD_ISSET(i, readset)) ||
+          (writeset && FD_ISSET(i, writeset)) ||
+          (exceptset && FD_ISSET(i, exceptset))) {
+        struct lwip_sock *sock;
+        SYS_ARCH_PROTECT(lev);
+        sock = tryget_socket(i);
+        if (sock != NULL) {
+          sock->select_waiting++;
+          LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0);
+        } else {
+          /* Not a valid socket */
+          nready = -1;
+          maxfdp2 = i;
+          SYS_ARCH_UNPROTECT(lev);
+          break;
+        }
+        SYS_ARCH_UNPROTECT(lev);
+      }
+    }
+
+    if (nready >= 0) {
+      /* Call lwip_selscan again: there could have been events between
+         the last scan (without us on the list) and putting us on the list! */
+      nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset);
+      if (!nready) {
+        /* Still none ready, just wait to be woken */
+        if (timeout == 0) {
+          /* Wait forever */
+          msectimeout = 0;
+        } else {
+          msectimeout =  ((timeout->tv_sec * 1000) + ((timeout->tv_usec + 500)/1000));
+          if (msectimeout == 0) {
+            /* Wait 1ms at least (0 means wait forever) */
+            msectimeout = 1;
+          }
+        }
+
+        waitres = sys_arch_sem_wait(SELECT_SEM_PTR(select_cb.sem), msectimeout);
+#if LWIP_NETCONN_SEM_PER_THREAD
+        waited = 1;
+#endif
+      }
+    }
+
+    /* Decrease select_waiting for each socket we are interested in */
+    for (i = LWIP_SOCKET_OFFSET; i < maxfdp2; i++) {
+      if ((readset && FD_ISSET(i, readset)) ||
+          (writeset && FD_ISSET(i, writeset)) ||
+          (exceptset && FD_ISSET(i, exceptset))) {
+        struct lwip_sock *sock;
+        SYS_ARCH_PROTECT(lev);
+        sock = tryget_socket(i);
+        if (sock != NULL) {
+          /* for now, handle select_waiting==0... */
+          LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0);
+          if (sock->select_waiting > 0) {
+            sock->select_waiting--;
+          }
+        } else {
+          /* Not a valid socket */
+          nready = -1;
+        }
+        SYS_ARCH_UNPROTECT(lev);
+      }
+    }
+    /* Take us off the list */
+    SYS_ARCH_PROTECT(lev);
+    if (select_cb.next != NULL) {
+      select_cb.next->prev = select_cb.prev;
+    }
+    if (select_cb_list == &select_cb) {
+      LWIP_ASSERT("select_cb.prev == NULL", select_cb.prev == NULL);
+      select_cb_list = select_cb.next;
+    } else {
+      LWIP_ASSERT("select_cb.prev != NULL", select_cb.prev != NULL);
+      select_cb.prev->next = select_cb.next;
+    }
+    /* Increasing this counter tells event_callback that the list has changed. */
+    select_cb_ctr++;
+    SYS_ARCH_UNPROTECT(lev);
+
+#if LWIP_NETCONN_SEM_PER_THREAD
+    if (select_cb.sem_signalled && (!waited || (waitres == SYS_ARCH_TIMEOUT))) {
+      /* don't leave the thread-local semaphore signalled */
+      sys_arch_sem_wait(select_cb.sem, 1);
+    }
+#else /* LWIP_NETCONN_SEM_PER_THREAD */
+    sys_sem_free(&select_cb.sem);
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+    if (nready < 0) {
+      /* This happens when a socket got closed while waiting */
+      set_errno(EBADF);
+      return -1;
+    }
+
+    if (waitres == SYS_ARCH_TIMEOUT) {
+      /* Timeout */
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: timeout expired\n"));
+      /* This is OK as the local fdsets are empty and nready is zero,
+         or we would have returned earlier. */
+      goto return_copy_fdsets;
+    }
+
+    /* See what's set */
+    nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset);
+  }
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: nready=%d\n", nready));
+return_copy_fdsets:
+  set_errno(0);
+  if (readset) {
+    *readset = lreadset;
+  }
+  if (writeset) {
+    *writeset = lwriteset;
+  }
+  if (exceptset) {
+    *exceptset = lexceptset;
+  }
+  return nready;
+}
+
+/**
+ * Callback registered in the netconn layer for each socket-netconn.
+ * Processes recvevent (data available) and wakes up tasks waiting for select.
+ */
+static void
+event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len)
+{
+  int s;
+  struct lwip_sock *sock;
+  struct lwip_select_cb *scb;
+  int last_select_cb_ctr;
+  SYS_ARCH_DECL_PROTECT(lev);
+
+  LWIP_UNUSED_ARG(len);
+
+  /* Get socket */
+  if (conn) {
+    s = conn->socket;
+    if (s < 0) {
+      /* Data comes in right away after an accept, even though
+       * the server task might not have created a new socket yet.
+       * Just count down (or up) if that's the case and we
+       * will use the data later. Note that only receive events
+       * can happen before the new socket is set up. */
+      SYS_ARCH_PROTECT(lev);
+      if (conn->socket < 0) {
+        if (evt == NETCONN_EVT_RCVPLUS) {
+          conn->socket--;
+        }
+        SYS_ARCH_UNPROTECT(lev);
+        return;
+      }
+      s = conn->socket;
+      SYS_ARCH_UNPROTECT(lev);
+    }
+
+    sock = get_socket(s);
+    if (!sock) {
+      return;
+    }
+  } else {
+    return;
+  }
+
+  SYS_ARCH_PROTECT(lev);
+  /* Set event as required */
+  switch (evt) {
+    case NETCONN_EVT_RCVPLUS:
+      sock->rcvevent++;
+      break;
+    case NETCONN_EVT_RCVMINUS:
+      sock->rcvevent--;
+      break;
+    case NETCONN_EVT_SENDPLUS:
+      sock->sendevent = 1;
+      break;
+    case NETCONN_EVT_SENDMINUS:
+      sock->sendevent = 0;
+      break;
+    case NETCONN_EVT_ERROR:
+      sock->errevent = 1;
+      break;
+    default:
+      LWIP_ASSERT("unknown event", 0);
+      break;
+  }
+
+  if (sock->select_waiting == 0) {
+    /* noone is waiting for this socket, no need to check select_cb_list */
+    SYS_ARCH_UNPROTECT(lev);
+    return;
+  }
+
+  /* Now decide if anyone is waiting for this socket */
+  /* NOTE: This code goes through the select_cb_list list multiple times
+     ONLY IF a select was actually waiting. We go through the list the number
+     of waiting select calls + 1. This list is expected to be small. */
+
+  /* At this point, SYS_ARCH is still protected! */
+again:
+  for (scb = select_cb_list; scb != NULL; scb = scb->next) {
+    /* remember the state of select_cb_list to detect changes */
+    last_select_cb_ctr = select_cb_ctr;
+    if (scb->sem_signalled == 0) {
+      /* semaphore not signalled yet */
+      int do_signal = 0;
+      /* Test this select call for our socket */
+      if (sock->rcvevent > 0) {
+        if (scb->readset && FD_ISSET(s, scb->readset)) {
+          do_signal = 1;
+        }
+      }
+      if (sock->sendevent != 0) {
+        if (!do_signal && scb->writeset && FD_ISSET(s, scb->writeset)) {
+          do_signal = 1;
+        }
+      }
+      if (sock->errevent != 0) {
+        if (!do_signal && scb->exceptset && FD_ISSET(s, scb->exceptset)) {
+          do_signal = 1;
+        }
+      }
+      if (do_signal) {
+        scb->sem_signalled = 1;
+        /* Don't call SYS_ARCH_UNPROTECT() before signaling the semaphore, as this might
+           lead to the select thread taking itself off the list, invalidating the semaphore. */
+        sys_sem_signal(SELECT_SEM_PTR(scb->sem));
+      }
+    }
+    /* unlock interrupts with each step */
+    SYS_ARCH_UNPROTECT(lev);
+    /* this makes sure interrupt protection time is short */
+    SYS_ARCH_PROTECT(lev);
+    if (last_select_cb_ctr != select_cb_ctr) {
+      /* someone has changed select_cb_list, restart at the beginning */
+      goto again;
+    }
+  }
+  SYS_ARCH_UNPROTECT(lev);
+}
+
+/**
+ * Close one end of a full-duplex connection.
+ */
+int
+lwip_shutdown(int s, int how)
+{
+  struct lwip_sock *sock;
+  err_t err;
+  u8_t shut_rx = 0, shut_tx = 0;
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_shutdown(%d, how=%d)\n", s, how));
+
+  sock = get_socket(s);
+  if (!sock) {
+    return -1;
+  }
+
+  if (sock->conn != NULL) {
+    if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
+      sock_set_errno(sock, EOPNOTSUPP);
+      return -1;
+    }
+  } else {
+    sock_set_errno(sock, ENOTCONN);
+    return -1;
+  }
+
+  if (how == SHUT_RD) {
+    shut_rx = 1;
+  } else if (how == SHUT_WR) {
+    shut_tx = 1;
+  } else if (how == SHUT_RDWR) {
+    shut_rx = 1;
+    shut_tx = 1;
+  } else {
+    sock_set_errno(sock, EINVAL);
+    return -1;
+  }
+  err = netconn_shutdown(sock->conn, shut_rx, shut_tx);
+
+  sock_set_errno(sock, err_to_errno(err));
+  return (err == ERR_OK ? 0 : -1);
+}
+
+static int
+lwip_getaddrname(int s, struct sockaddr *name, socklen_t *namelen, u8_t local)
+{
+  struct lwip_sock *sock;
+  union sockaddr_aligned saddr;
+  ip_addr_t naddr;
+  u16_t port;
+  err_t err;
+
+  sock = get_socket(s);
+  if (!sock) {
+    return -1;
+  }
+
+  /* get the IP address and port */
+  err = netconn_getaddr(sock->conn, &naddr, &port, local);
+  if (err != ERR_OK) {
+    sock_set_errno(sock, err_to_errno(err));
+    return -1;
+  }
+
+#if LWIP_IPV4 && LWIP_IPV6
+  /* Dual-stack: Map IPv4 addresses to IPv4 mapped IPv6 */
+  if (NETCONNTYPE_ISIPV6(netconn_type(sock->conn)) &&
+      IP_IS_V4_VAL(naddr)) {
+    ip4_2_ipv4_mapped_ipv6(ip_2_ip6(&naddr), ip_2_ip4(&naddr));
+    IP_SET_TYPE_VAL(naddr, IPADDR_TYPE_V6);
+  }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+  IPADDR_PORT_TO_SOCKADDR(&saddr, &naddr, port);
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getaddrname(%d, addr=", s));
+  ip_addr_debug_print_val(SOCKETS_DEBUG, naddr);
+  LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", port));
+
+  if (*namelen > saddr.sa.sa_len) {
+    *namelen = saddr.sa.sa_len;
+  }
+  MEMCPY(name, &saddr, *namelen);
+
+  sock_set_errno(sock, 0);
+  return 0;
+}
+
+int
+lwip_getpeername(int s, struct sockaddr *name, socklen_t *namelen)
+{
+  return lwip_getaddrname(s, name, namelen, 0);
+}
+
+int
+lwip_getsockname(int s, struct sockaddr *name, socklen_t *namelen)
+{
+  return lwip_getaddrname(s, name, namelen, 1);
+}
+
+int
+lwip_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen)
+{
+  u8_t err;
+  struct lwip_sock *sock = get_socket(s);
+#if !LWIP_TCPIP_CORE_LOCKING
+  LWIP_SETGETSOCKOPT_DATA_VAR_DECLARE(data);
+#endif /* !LWIP_TCPIP_CORE_LOCKING */
+
+  if (!sock) {
+    return -1;
+  }
+
+  if ((NULL == optval) || (NULL == optlen)) {
+    sock_set_errno(sock, EFAULT);
+    return -1;
+  }
+
+#if LWIP_TCPIP_CORE_LOCKING
+  /* core-locking can just call the -impl function */
+  LOCK_TCPIP_CORE();
+  err = lwip_getsockopt_impl(s, level, optname, optval, optlen);
+  UNLOCK_TCPIP_CORE();
+
+#else /* LWIP_TCPIP_CORE_LOCKING */
+
+#if LWIP_MPU_COMPATIBLE
+  /* MPU_COMPATIBLE copies the optval data, so check for max size here */
+  if (*optlen > LWIP_SETGETSOCKOPT_MAXOPTLEN) {
+    sock_set_errno(sock, ENOBUFS);
+    return -1;
+  }
+#endif /* LWIP_MPU_COMPATIBLE */
+
+  LWIP_SETGETSOCKOPT_DATA_VAR_ALLOC(data, sock);
+  LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).s = s;
+  LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).level = level;
+  LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optname = optname;
+  LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen = *optlen;
+#if !LWIP_MPU_COMPATIBLE
+  LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval.p = optval;
+#endif /* !LWIP_MPU_COMPATIBLE */
+  LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err = 0;
+#if LWIP_NETCONN_SEM_PER_THREAD
+  LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = LWIP_NETCONN_THREAD_SEM_GET();
+#else
+  LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = &sock->conn->op_completed;
+#endif
+  err = tcpip_callback(lwip_getsockopt_callback, &LWIP_SETGETSOCKOPT_DATA_VAR_REF(data));
+  if (err != ERR_OK) {
+    LWIP_SETGETSOCKOPT_DATA_VAR_FREE(data);
+    sock_set_errno(sock, err_to_errno(err));
+    return -1;
+  }
+  sys_arch_sem_wait((sys_sem_t*)(LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem), 0);
+
+  /* write back optlen and optval */
+  *optlen = LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen;
+#if LWIP_MPU_COMPATIBLE
+  MEMCPY(optval, LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval,
+    LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen);
+#endif /* LWIP_MPU_COMPATIBLE */
+
+  /* maybe lwip_getsockopt_internal has changed err */
+  err = LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err;
+  LWIP_SETGETSOCKOPT_DATA_VAR_FREE(data);
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+
+  sock_set_errno(sock, err);
+  return err ? -1 : 0;
+}
+
+#if !LWIP_TCPIP_CORE_LOCKING
+/** lwip_getsockopt_callback: only used without CORE_LOCKING
+ * to get into the tcpip_thread
+ */
+static void
+lwip_getsockopt_callback(void *arg)
+{
+  struct lwip_setgetsockopt_data *data;
+  LWIP_ASSERT("arg != NULL", arg != NULL);
+  data = (struct lwip_setgetsockopt_data*)arg;
+
+  data->err = lwip_getsockopt_impl(data->s, data->level, data->optname,
+#if LWIP_MPU_COMPATIBLE
+    data->optval,
+#else /* LWIP_MPU_COMPATIBLE */
+    data->optval.p,
+#endif /* LWIP_MPU_COMPATIBLE */
+    &data->optlen);
+
+  sys_sem_signal((sys_sem_t*)(data->completed_sem));
+}
+#endif  /* LWIP_TCPIP_CORE_LOCKING */
+
+/** lwip_getsockopt_impl: the actual implementation of getsockopt:
+ * same argument as lwip_getsockopt, either called directly or through callback
+ */
+static u8_t
+lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *optlen)
+{
+  u8_t err = 0;
+  struct lwip_sock *sock = tryget_socket(s);
+  if (!sock) {
+    return EBADF;
+  }
+
+  switch (level) {
+
+/* Level: SOL_SOCKET */
+  case SOL_SOCKET:
+    switch (optname) {
+
+#if LWIP_TCP
+    case SO_ACCEPTCONN:
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
+      if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_TCP) {
+        return ENOPROTOOPT;
+      }
+      if ((sock->conn->pcb.tcp != NULL) && (sock->conn->pcb.tcp->state == LISTEN)) {
+        *(int*)optval = 1;
+      } else {
+        *(int*)optval = 0;
+      }
+      break;
+#endif /* LWIP_TCP */
+
+    /* The option flags */
+    case SO_BROADCAST:
+    case SO_KEEPALIVE:
+#if SO_REUSE
+    case SO_REUSEADDR:
+#endif /* SO_REUSE */
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
+      *(int*)optval = ip_get_option(sock->conn->pcb.ip, optname);
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, optname=0x%x, ..) = %s\n",
+                                  s, optname, (*(int*)optval?"on":"off")));
+      break;
+
+    case SO_TYPE:
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, int);
+      switch (NETCONNTYPE_GROUP(netconn_type(sock->conn))) {
+      case NETCONN_RAW:
+        *(int*)optval = SOCK_RAW;
+        break;
+      case NETCONN_TCP:
+        *(int*)optval = SOCK_STREAM;
+        break;
+      case NETCONN_UDP:
+        *(int*)optval = SOCK_DGRAM;
+        break;
+      default: /* unrecognized socket type */
+        *(int*)optval = netconn_type(sock->conn);
+        LWIP_DEBUGF(SOCKETS_DEBUG,
+                    ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE): unrecognized socket type %d\n",
+                    s, *(int *)optval));
+      }  /* switch (netconn_type(sock->conn)) */
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE) = %d\n",
+                  s, *(int *)optval));
+      break;
+
+    case SO_ERROR:
+      LWIP_SOCKOPT_CHECK_OPTLEN(*optlen, int);
+      /* only overwrite ERR_OK or temporary errors */
+      if (((sock->err == 0) || (sock->err == EINPROGRESS)) && (sock->conn != NULL)) {
+        sock_set_errno(sock, err_to_errno(sock->conn->last_err));
+      }
+      *(int *)optval = (sock->err == 0xFF ? (int)-1 : (int)sock->err);
+      sock->err = 0;
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_ERROR) = %d\n",
+                  s, *(int *)optval));
+      break;
+
+#if LWIP_SO_SNDTIMEO
+    case SO_SNDTIMEO:
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE);
+      LWIP_SO_SNDRCVTIMEO_SET(optval, netconn_get_sendtimeout(sock->conn));
+      break;
+#endif /* LWIP_SO_SNDTIMEO */
+#if LWIP_SO_RCVTIMEO
+    case SO_RCVTIMEO:
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE);
+      LWIP_SO_SNDRCVTIMEO_SET(optval, netconn_get_recvtimeout(sock->conn));
+      break;
+#endif /* LWIP_SO_RCVTIMEO */
+#if LWIP_SO_RCVBUF
+    case SO_RCVBUF:
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, int);
+      *(int *)optval = netconn_get_recvbufsize(sock->conn);
+      break;
+#endif /* LWIP_SO_RCVBUF */
+#if LWIP_SO_LINGER
+    case SO_LINGER:
+      {
+        s16_t conn_linger;
+        struct linger* linger = (struct linger*)optval;
+        LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, struct linger);
+        conn_linger = sock->conn->linger;
+        if (conn_linger >= 0) {
+          linger->l_onoff = 1;
+          linger->l_linger = (int)conn_linger;
+        } else {
+          linger->l_onoff = 0;
+          linger->l_linger = 0;
+        }
+      }
+      break;
+#endif /* LWIP_SO_LINGER */
+#if LWIP_UDP
+    case SO_NO_CHECK:
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, int, NETCONN_UDP);
+#if LWIP_UDPLITE
+      if ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0) {
+        /* this flag is only available for UDP, not for UDP lite */
+        return EAFNOSUPPORT;
+      }
+#endif /* LWIP_UDPLITE */
+      *(int*)optval = (udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_NOCHKSUM) ? 1 : 0;
+      break;
+#endif /* LWIP_UDP*/
+    default:
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n",
+                  s, optname));
+      err = ENOPROTOOPT;
+      break;
+    }  /* switch (optname) */
+    break;
+
+/* Level: IPPROTO_IP */
+  case IPPROTO_IP:
+    switch (optname) {
+    case IP_TTL:
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
+      *(int*)optval = sock->conn->pcb.ip->ttl;
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TTL) = %d\n",
+                  s, *(int *)optval));
+      break;
+    case IP_TOS:
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
+      *(int*)optval = sock->conn->pcb.ip->tos;
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TOS) = %d\n",
+                  s, *(int *)optval));
+      break;
+#if LWIP_MULTICAST_TX_OPTIONS
+    case IP_MULTICAST_TTL:
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, u8_t);
+      if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
+        return ENOPROTOOPT;
+      }
+      *(u8_t*)optval = udp_get_multicast_ttl(sock->conn->pcb.udp);
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_TTL) = %d\n",
+                  s, *(int *)optval));
+      break;
+    case IP_MULTICAST_IF:
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, struct in_addr);
+      if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
+        return ENOPROTOOPT;
+      }
+      inet_addr_from_ip4addr((struct in_addr*)optval, udp_get_multicast_netif_addr(sock->conn->pcb.udp));
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_IF) = 0x%"X32_F"\n",
+                  s, *(u32_t *)optval));
+      break;
+    case IP_MULTICAST_LOOP:
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, u8_t);
+      if ((sock->conn->pcb.udp->flags & UDP_FLAGS_MULTICAST_LOOP) != 0) {
+        *(u8_t*)optval = 1;
+      } else {
+        *(u8_t*)optval = 0;
+      }
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_LOOP) = %d\n",
+                  s, *(int *)optval));
+      break;
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
+    default:
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n",
+                  s, optname));
+      err = ENOPROTOOPT;
+      break;
+    }  /* switch (optname) */
+    break;
+
+#if LWIP_TCP
+/* Level: IPPROTO_TCP */
+  case IPPROTO_TCP:
+    /* Special case: all IPPROTO_TCP option take an int */
+    LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, int, NETCONN_TCP);
+    if (sock->conn->pcb.tcp->state == LISTEN) {
+      return EINVAL;
+    }
+    switch (optname) {
+    case TCP_NODELAY:
+      *(int*)optval = tcp_nagle_disabled(sock->conn->pcb.tcp);
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_NODELAY) = %s\n",
+                  s, (*(int*)optval)?"on":"off") );
+      break;
+    case TCP_KEEPALIVE:
+      *(int*)optval = (int)sock->conn->pcb.tcp->keep_idle;
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_KEEPALIVE) = %d\n",
+                  s, *(int *)optval));
+      break;
+
+#if LWIP_TCP_KEEPALIVE
+    case TCP_KEEPIDLE:
+      *(int*)optval = (int)(sock->conn->pcb.tcp->keep_idle/1000);
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_KEEPIDLE) = %d\n",
+                  s, *(int *)optval));
+      break;
+    case TCP_KEEPINTVL:
+      *(int*)optval = (int)(sock->conn->pcb.tcp->keep_intvl/1000);
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_KEEPINTVL) = %d\n",
+                  s, *(int *)optval));
+      break;
+    case TCP_KEEPCNT:
+      *(int*)optval = (int)sock->conn->pcb.tcp->keep_cnt;
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_KEEPCNT) = %d\n",
+                  s, *(int *)optval));
+      break;
+#endif /* LWIP_TCP_KEEPALIVE */
+    default:
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n",
+                  s, optname));
+      err = ENOPROTOOPT;
+      break;
+    }  /* switch (optname) */
+    break;
+#endif /* LWIP_TCP */
+
+#if LWIP_IPV6
+/* Level: IPPROTO_IPV6 */
+  case IPPROTO_IPV6:
+    switch (optname) {
+    case IPV6_V6ONLY:
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, int);
+      *(int*)optval = (netconn_get_ipv6only(sock->conn) ? 1 : 0);
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY) = %d\n",
+                  s, *(int *)optval));
+      break;
+    default:
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, UNIMPL: optname=0x%x, ..)\n",
+                  s, optname));
+      err = ENOPROTOOPT;
+      break;
+    }  /* switch (optname) */
+    break;
+#endif /* LWIP_IPV6 */
+
+#if LWIP_UDP && LWIP_UDPLITE
+  /* Level: IPPROTO_UDPLITE */
+  case IPPROTO_UDPLITE:
+    /* Special case: all IPPROTO_UDPLITE option take an int */
+    LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
+    /* If this is no UDP lite socket, ignore any options. */
+    if (!NETCONNTYPE_ISUDPLITE(netconn_type(sock->conn))) {
+      return ENOPROTOOPT;
+    }
+    switch (optname) {
+    case UDPLITE_SEND_CSCOV:
+      *(int*)optval = sock->conn->pcb.udp->chksum_len_tx;
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV) = %d\n",
+                  s, (*(int*)optval)) );
+      break;
+    case UDPLITE_RECV_CSCOV:
+      *(int*)optval = sock->conn->pcb.udp->chksum_len_rx;
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV) = %d\n",
+                  s, (*(int*)optval)) );
+      break;
+    default:
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n",
+                  s, optname));
+      err = ENOPROTOOPT;
+      break;
+    }  /* switch (optname) */
+    break;
+#endif /* LWIP_UDP */
+  /* Level: IPPROTO_RAW */
+  case IPPROTO_RAW:
+    switch (optname) {
+#if LWIP_IPV6 && LWIP_RAW
+    case IPV6_CHECKSUM:
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, int, NETCONN_RAW);
+      if (sock->conn->pcb.raw->chksum_reqd == 0) {
+        *(int *)optval = -1;
+      } else {
+        *(int *)optval = sock->conn->pcb.raw->chksum_offset;
+      }
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_RAW, IPV6_CHECKSUM) = %d\n",
+                  s, (*(int*)optval)) );
+      break;
+#endif /* LWIP_IPV6 && LWIP_RAW */
+    default:
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_RAW, UNIMPL: optname=0x%x, ..)\n",
+                  s, optname));
+      err = ENOPROTOOPT;
+      break;
+    }  /* switch (optname) */
+    break;
+  default:
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n",
+                                s, level, optname));
+    err = ENOPROTOOPT;
+    break;
+  } /* switch (level) */
+
+  return err;
+}
+
+int
+lwip_setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen)
+{
+  u8_t err = 0;
+  struct lwip_sock *sock = get_socket(s);
+#if !LWIP_TCPIP_CORE_LOCKING
+  LWIP_SETGETSOCKOPT_DATA_VAR_DECLARE(data);
+#endif /* !LWIP_TCPIP_CORE_LOCKING */
+
+  if (!sock) {
+    return -1;
+  }
+
+  if (NULL == optval) {
+    sock_set_errno(sock, EFAULT);
+    return -1;
+  }
+
+#if LWIP_TCPIP_CORE_LOCKING
+  /* core-locking can just call the -impl function */
+  LOCK_TCPIP_CORE();
+  err = lwip_setsockopt_impl(s, level, optname, optval, optlen);
+  UNLOCK_TCPIP_CORE();
+
+#else /* LWIP_TCPIP_CORE_LOCKING */
+
+#if LWIP_MPU_COMPATIBLE
+  /* MPU_COMPATIBLE copies the optval data, so check for max size here */
+  if (optlen > LWIP_SETGETSOCKOPT_MAXOPTLEN) {
+    sock_set_errno(sock, ENOBUFS);
+    return -1;
+  }
+#endif /* LWIP_MPU_COMPATIBLE */
+
+  LWIP_SETGETSOCKOPT_DATA_VAR_ALLOC(data, sock);
+  LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).s = s;
+  LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).level = level;
+  LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optname = optname;
+  LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen = optlen;
+#if LWIP_MPU_COMPATIBLE
+  MEMCPY(LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval, optval, optlen);
+#else /* LWIP_MPU_COMPATIBLE */
+  LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval.pc = (const void*)optval;
+#endif /* LWIP_MPU_COMPATIBLE */
+  LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err = 0;
+#if LWIP_NETCONN_SEM_PER_THREAD
+  LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = LWIP_NETCONN_THREAD_SEM_GET();
+#else
+  LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = &sock->conn->op_completed;
+#endif
+  err = tcpip_callback(lwip_setsockopt_callback, &LWIP_SETGETSOCKOPT_DATA_VAR_REF(data));
+  if (err != ERR_OK) {
+    LWIP_SETGETSOCKOPT_DATA_VAR_FREE(data);
+    sock_set_errno(sock, err_to_errno(err));
+    return -1;
+  }
+  sys_arch_sem_wait((sys_sem_t*)(LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem), 0);
+
+  /* maybe lwip_getsockopt_internal has changed err */
+  err = LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err;
+  LWIP_SETGETSOCKOPT_DATA_VAR_FREE(data);
+#endif  /* LWIP_TCPIP_CORE_LOCKING */
+
+  sock_set_errno(sock, err);
+  return err ? -1 : 0;
+}
+
+#if !LWIP_TCPIP_CORE_LOCKING
+/** lwip_setsockopt_callback: only used without CORE_LOCKING
+ * to get into the tcpip_thread
+ */
+static void
+lwip_setsockopt_callback(void *arg)
+{
+  struct lwip_setgetsockopt_data *data;
+  LWIP_ASSERT("arg != NULL", arg != NULL);
+  data = (struct lwip_setgetsockopt_data*)arg;
+
+  data->err = lwip_setsockopt_impl(data->s, data->level, data->optname,
+#if LWIP_MPU_COMPATIBLE
+    data->optval,
+#else /* LWIP_MPU_COMPATIBLE */
+    data->optval.pc,
+#endif /* LWIP_MPU_COMPATIBLE */
+    data->optlen);
+
+  sys_sem_signal((sys_sem_t*)(data->completed_sem));
+}
+#endif  /* LWIP_TCPIP_CORE_LOCKING */
+
+/** lwip_setsockopt_impl: the actual implementation of setsockopt:
+ * same argument as lwip_setsockopt, either called directly or through callback
+ */
+static u8_t
+lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_t optlen)
+{
+  u8_t err = 0;
+  struct lwip_sock *sock = tryget_socket(s);
+  if (!sock) {
+    return EBADF;
+  }
+
+  switch (level) {
+
+/* Level: SOL_SOCKET */
+  case SOL_SOCKET:
+    switch (optname) {
+
+    /* SO_ACCEPTCONN is get-only */
+
+    /* The option flags */
+    case SO_BROADCAST:
+    case SO_KEEPALIVE:
+#if SO_REUSE
+    case SO_REUSEADDR:
+#endif /* SO_REUSE */
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
+      if (*(const int*)optval) {
+        ip_set_option(sock->conn->pcb.ip, optname);
+      } else {
+        ip_reset_option(sock->conn->pcb.ip, optname);
+      }
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, optname=0x%x, ..) -> %s\n",
+                  s, optname, (*(const int*)optval?"on":"off")));
+      break;
+
+    /* SO_TYPE is get-only */
+    /* SO_ERROR is get-only */
+
+#if LWIP_SO_SNDTIMEO
+    case SO_SNDTIMEO:
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE);
+      netconn_set_sendtimeout(sock->conn, LWIP_SO_SNDRCVTIMEO_GET_MS(optval));
+      break;
+#endif /* LWIP_SO_SNDTIMEO */
+#if LWIP_SO_RCVTIMEO
+    case SO_RCVTIMEO:
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE);
+      netconn_set_recvtimeout(sock->conn, (int)LWIP_SO_SNDRCVTIMEO_GET_MS(optval));
+      break;
+#endif /* LWIP_SO_RCVTIMEO */
+#if LWIP_SO_RCVBUF
+    case SO_RCVBUF:
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, int);
+      netconn_set_recvbufsize(sock->conn, *(const int*)optval);
+      break;
+#endif /* LWIP_SO_RCVBUF */
+#if LWIP_SO_LINGER
+    case SO_LINGER:
+      {
+        const struct linger* linger = (const struct linger*)optval;
+        LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, struct linger);
+        if (linger->l_onoff) {
+          int lingersec = linger->l_linger;
+          if (lingersec < 0) {
+            return EINVAL;
+          }
+          if (lingersec > 0xFFFF) {
+            lingersec = 0xFFFF;
+          }
+          sock->conn->linger = (s16_t)lingersec;
+        } else {
+          sock->conn->linger = -1;
+        }
+      }
+      break;
+#endif /* LWIP_SO_LINGER */
+#if LWIP_UDP
+    case SO_NO_CHECK:
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_UDP);
+#if LWIP_UDPLITE
+      if ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0) {
+        /* this flag is only available for UDP, not for UDP lite */
+        return EAFNOSUPPORT;
+      }
+#endif /* LWIP_UDPLITE */
+      if (*(const int*)optval) {
+        udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) | UDP_FLAGS_NOCHKSUM);
+      } else {
+        udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) & ~UDP_FLAGS_NOCHKSUM);
+      }
+      break;
+#endif /* LWIP_UDP */
+    default:
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n",
+                  s, optname));
+      err = ENOPROTOOPT;
+      break;
+    }  /* switch (optname) */
+    break;
+
+/* Level: IPPROTO_IP */
+  case IPPROTO_IP:
+    switch (optname) {
+    case IP_TTL:
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
+      sock->conn->pcb.ip->ttl = (u8_t)(*(const int*)optval);
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TTL, ..) -> %d\n",
+                  s, sock->conn->pcb.ip->ttl));
+      break;
+    case IP_TOS:
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
+      sock->conn->pcb.ip->tos = (u8_t)(*(const int*)optval);
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TOS, ..)-> %d\n",
+                  s, sock->conn->pcb.ip->tos));
+      break;
+#if LWIP_MULTICAST_TX_OPTIONS
+    case IP_MULTICAST_TTL:
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, u8_t, NETCONN_UDP);
+      udp_set_multicast_ttl(sock->conn->pcb.udp, (u8_t)(*(const u8_t*)optval));
+      break;
+    case IP_MULTICAST_IF:
+      {
+        ip4_addr_t if_addr;
+        LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, struct in_addr, NETCONN_UDP);
+        inet_addr_to_ip4addr(&if_addr, (const struct in_addr*)optval);
+        udp_set_multicast_netif_addr(sock->conn->pcb.udp, &if_addr);
+      }
+      break;
+    case IP_MULTICAST_LOOP:
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, u8_t, NETCONN_UDP);
+      if (*(const u8_t*)optval) {
+        udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) | UDP_FLAGS_MULTICAST_LOOP);
+      } else {
+        udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) & ~UDP_FLAGS_MULTICAST_LOOP);
+      }
+      break;
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
+#if LWIP_IGMP
+    case IP_ADD_MEMBERSHIP:
+    case IP_DROP_MEMBERSHIP:
+      {
+        /* If this is a TCP or a RAW socket, ignore these options. */
+        /* @todo: assign membership to this socket so that it is dropped when closing the socket */
+        err_t igmp_err;
+        const struct ip_mreq *imr = (const struct ip_mreq *)optval;
+        ip4_addr_t if_addr;
+        ip4_addr_t multi_addr;
+        LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, struct ip_mreq, NETCONN_UDP);
+        inet_addr_to_ip4addr(&if_addr, &imr->imr_interface);
+        inet_addr_to_ip4addr(&multi_addr, &imr->imr_multiaddr);
+        if (optname == IP_ADD_MEMBERSHIP) {
+          if (!lwip_socket_register_membership(s, &if_addr, &multi_addr)) {
+            /* cannot track membership (out of memory) */
+            err = ENOMEM;
+            igmp_err = ERR_OK;
+          } else {
+            igmp_err = igmp_joingroup(&if_addr, &multi_addr);
+          }
+        } else {
+          igmp_err = igmp_leavegroup(&if_addr, &multi_addr);
+          lwip_socket_unregister_membership(s, &if_addr, &multi_addr);
+        }
+        if (igmp_err != ERR_OK) {
+          err = EADDRNOTAVAIL;
+        }
+      }
+      break;
+#endif /* LWIP_IGMP */
+    default:
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n",
+                  s, optname));
+      err = ENOPROTOOPT;
+      break;
+    }  /* switch (optname) */
+    break;
+
+#if LWIP_TCP
+/* Level: IPPROTO_TCP */
+  case IPPROTO_TCP:
+    /* Special case: all IPPROTO_TCP option take an int */
+    LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_TCP);
+    if (sock->conn->pcb.tcp->state == LISTEN) {
+      return EINVAL;
+    }
+    switch (optname) {
+    case TCP_NODELAY:
+      if (*(const int*)optval) {
+        tcp_nagle_disable(sock->conn->pcb.tcp);
+      } else {
+        tcp_nagle_enable(sock->conn->pcb.tcp);
+      }
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_NODELAY) -> %s\n",
+                  s, (*(const int *)optval)?"on":"off") );
+      break;
+    case TCP_KEEPALIVE:
+      sock->conn->pcb.tcp->keep_idle = (u32_t)(*(const int*)optval);
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPALIVE) -> %"U32_F"\n",
+                  s, sock->conn->pcb.tcp->keep_idle));
+      break;
+
+#if LWIP_TCP_KEEPALIVE
+    case TCP_KEEPIDLE:
+      sock->conn->pcb.tcp->keep_idle = 1000*(u32_t)(*(const int*)optval);
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPIDLE) -> %"U32_F"\n",
+                  s, sock->conn->pcb.tcp->keep_idle));
+      break;
+    case TCP_KEEPINTVL:
+      sock->conn->pcb.tcp->keep_intvl = 1000*(u32_t)(*(const int*)optval);
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPINTVL) -> %"U32_F"\n",
+                  s, sock->conn->pcb.tcp->keep_intvl));
+      break;
+    case TCP_KEEPCNT:
+      sock->conn->pcb.tcp->keep_cnt = (u32_t)(*(const int*)optval);
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPCNT) -> %"U32_F"\n",
+                  s, sock->conn->pcb.tcp->keep_cnt));
+      break;
+#endif /* LWIP_TCP_KEEPALIVE */
+    default:
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n",
+                  s, optname));
+      err = ENOPROTOOPT;
+      break;
+    }  /* switch (optname) */
+    break;
+#endif /* LWIP_TCP*/
+
+#if LWIP_IPV6
+/* Level: IPPROTO_IPV6 */
+  case IPPROTO_IPV6:
+    switch (optname) {
+    case IPV6_V6ONLY:
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_TCP);
+      if (*(const int*)optval) {
+        netconn_set_ipv6only(sock->conn, 1);
+      } else {
+        netconn_set_ipv6only(sock->conn, 0);
+      }
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY, ..) -> %d\n",
+                  s, (netconn_get_ipv6only(sock->conn) ? 1 : 0)));
+      break;
+    default:
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, UNIMPL: optname=0x%x, ..)\n",
+                  s, optname));
+      err = ENOPROTOOPT;
+      break;
+    }  /* switch (optname) */
+    break;
+#endif /* LWIP_IPV6 */
+
+#if LWIP_UDP && LWIP_UDPLITE
+  /* Level: IPPROTO_UDPLITE */
+  case IPPROTO_UDPLITE:
+    /* Special case: all IPPROTO_UDPLITE option take an int */
+    LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
+    /* If this is no UDP lite socket, ignore any options. */
+    if (!NETCONNTYPE_ISUDPLITE(netconn_type(sock->conn))) {
+      return ENOPROTOOPT;
+    }
+    switch (optname) {
+    case UDPLITE_SEND_CSCOV:
+      if ((*(const int*)optval != 0) && ((*(const int*)optval < 8) || (*(const int*)optval > 0xffff))) {
+        /* don't allow illegal values! */
+        sock->conn->pcb.udp->chksum_len_tx = 8;
+      } else {
+        sock->conn->pcb.udp->chksum_len_tx = (u16_t)*(const int*)optval;
+      }
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV) -> %d\n",
+                  s, (*(const int*)optval)) );
+      break;
+    case UDPLITE_RECV_CSCOV:
+      if ((*(const int*)optval != 0) && ((*(const int*)optval < 8) || (*(const int*)optval > 0xffff))) {
+        /* don't allow illegal values! */
+        sock->conn->pcb.udp->chksum_len_rx = 8;
+      } else {
+        sock->conn->pcb.udp->chksum_len_rx = (u16_t)*(const int*)optval;
+      }
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV) -> %d\n",
+                  s, (*(const int*)optval)) );
+      break;
+    default:
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n",
+                  s, optname));
+      err = ENOPROTOOPT;
+      break;
+    }  /* switch (optname) */
+    break;
+#endif /* LWIP_UDP */
+  /* Level: IPPROTO_RAW */
+  case IPPROTO_RAW:
+    switch (optname) {
+#if LWIP_IPV6 && LWIP_RAW
+    case IPV6_CHECKSUM:
+      /* It should not be possible to disable the checksum generation with ICMPv6
+       * as per RFC 3542 chapter 3.1 */
+      if(sock->conn->pcb.raw->protocol == IPPROTO_ICMPV6) {
+        return EINVAL;
+      }
+
+      LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_RAW);
+      if (*(const int *)optval < 0) {
+        sock->conn->pcb.raw->chksum_reqd = 0;
+      } else if (*(const int *)optval & 1) {
+        /* Per RFC3542, odd offsets are not allowed */
+        return EINVAL;
+      } else {
+        sock->conn->pcb.raw->chksum_reqd = 1;
+        sock->conn->pcb.raw->chksum_offset = (u16_t)*(const int *)optval;
+      }
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_RAW, IPV6_CHECKSUM, ..) -> %d\n",
+                  s, sock->conn->pcb.raw->chksum_reqd));
+      break;
+#endif /* LWIP_IPV6 && LWIP_RAW */
+    default:
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_RAW, UNIMPL: optname=0x%x, ..)\n",
+                                  s, optname));
+      err = ENOPROTOOPT;
+      break;
+    }  /* switch (optname) */
+    break;
+  default:
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n",
+                s, level, optname));
+    err = ENOPROTOOPT;
+    break;
+  }  /* switch (level) */
+
+  return err;
+}
+
+int
+lwip_ioctl(int s, long cmd, void *argp)
+{
+  struct lwip_sock *sock = get_socket(s);
+  u8_t val;
+#if LWIP_SO_RCVBUF
+  u16_t buflen = 0;
+  int recv_avail;
+#endif /* LWIP_SO_RCVBUF */
+
+  if (!sock) {
+    return -1;
+  }
+
+  switch (cmd) {
+#if LWIP_SO_RCVBUF || LWIP_FIONREAD_LINUXMODE
+  case FIONREAD:
+    if (!argp) {
+      sock_set_errno(sock, EINVAL);
+      return -1;
+    }
+#if LWIP_FIONREAD_LINUXMODE
+    if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
+      struct pbuf *p;
+      if (sock->lastdata) {
+        p = ((struct netbuf *)sock->lastdata)->p;
+        *((int*)argp) = p->tot_len - sock->lastoffset;
+      } else {
+        struct netbuf *rxbuf;
+        err_t err;
+        if (sock->rcvevent <= 0) {
+          *((int*)argp) = 0;
+        } else {
+          err = netconn_recv(sock->conn, &rxbuf);
+          if (err != ERR_OK) {
+            *((int*)argp) = 0;
+          } else {
+            sock->lastdata = rxbuf;
+            sock->lastoffset = 0;
+            *((int*)argp) = rxbuf->p->tot_len;
+          }
+        }
+      }
+      return 0;
+    }
+#endif /* LWIP_FIONREAD_LINUXMODE */
+
+#if LWIP_SO_RCVBUF
+    /* we come here if either LWIP_FIONREAD_LINUXMODE==0 or this is a TCP socket */
+    SYS_ARCH_GET(sock->conn->recv_avail, recv_avail);
+    if (recv_avail < 0) {
+      recv_avail = 0;
+    }
+    *((int*)argp) = recv_avail;
+
+    /* Check if there is data left from the last recv operation. /maq 041215 */
+    if (sock->lastdata) {
+      struct pbuf *p = (struct pbuf *)sock->lastdata;
+      if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
+        p = ((struct netbuf *)p)->p;
+      }
+      buflen = p->tot_len;
+      buflen -= sock->lastoffset;
+
+      *((int*)argp) += buflen;
+    }
+
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONREAD, %p) = %"U16_F"\n", s, argp, *((u16_t*)argp)));
+    sock_set_errno(sock, 0);
+    return 0;
+#else /* LWIP_SO_RCVBUF */
+    break;
+#endif /* LWIP_SO_RCVBUF */
+#endif /* LWIP_SO_RCVBUF || LWIP_FIONREAD_LINUXMODE */
+
+  case (long)FIONBIO:
+    val = 0;
+    if (argp && *(u32_t*)argp) {
+      val = 1;
+    }
+    netconn_set_nonblocking(sock->conn, val);
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONBIO, %d)\n", s, val));
+    sock_set_errno(sock, 0);
+    return 0;
+
+  default:
+    break;
+  } /* switch (cmd) */
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, UNIMPL: 0x%lx, %p)\n", s, cmd, argp));
+  sock_set_errno(sock, ENOSYS); /* not yet implemented */
+  return -1;
+}
+
+/** A minimal implementation of fcntl.
+ * Currently only the commands F_GETFL and F_SETFL are implemented.
+ * Only the flag O_NONBLOCK is implemented.
+ */
+int
+lwip_fcntl(int s, int cmd, int val)
+{
+  struct lwip_sock *sock = get_socket(s);
+  int ret = -1;
+
+  if (!sock) {
+    return -1;
+  }
+
+  switch (cmd) {
+  case F_GETFL:
+    ret = netconn_is_nonblocking(sock->conn) ? O_NONBLOCK : 0;
+    sock_set_errno(sock, 0);
+    break;
+  case F_SETFL:
+    if ((val & ~O_NONBLOCK) == 0) {
+      /* only O_NONBLOCK, all other bits are zero */
+      netconn_set_nonblocking(sock->conn, val & O_NONBLOCK);
+      ret = 0;
+      sock_set_errno(sock, 0);
+    } else {
+      sock_set_errno(sock, ENOSYS); /* not yet implemented */
+    }
+    break;
+  default:
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_fcntl(%d, UNIMPL: %d, %d)\n", s, cmd, val));
+    sock_set_errno(sock, ENOSYS); /* not yet implemented */
+    break;
+  }
+  return ret;
+}
+
+#if LWIP_IGMP
+/** Register a new IGMP membership. On socket close, the membership is dropped automatically.
+ *
+ * ATTENTION: this function is called from tcpip_thread (or under CORE_LOCK).
+ *
+ * @return 1 on success, 0 on failure
+ */
+static int
+lwip_socket_register_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr)
+{
+  struct lwip_sock *sock = get_socket(s);
+  int i;
+
+  if (!sock) {
+    return 0;
+  }
+
+  for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
+    if (socket_ipv4_multicast_memberships[i].sock == NULL) {
+      socket_ipv4_multicast_memberships[i].sock = sock;
+      ip4_addr_copy(socket_ipv4_multicast_memberships[i].if_addr, *if_addr);
+      ip4_addr_copy(socket_ipv4_multicast_memberships[i].multi_addr, *multi_addr);
+      return 1;
+    }
+  }
+  return 0;
+}
+
+/** Unregister a previously registered membership. This prevents dropping the membership
+ * on socket close.
+ *
+ * ATTENTION: this function is called from tcpip_thread (or under CORE_LOCK).
+ */
+static void
+lwip_socket_unregister_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr)
+{
+  struct lwip_sock *sock = get_socket(s);
+  int i;
+
+  if (!sock) {
+    return;
+  }
+
+  for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
+    if ((socket_ipv4_multicast_memberships[i].sock == sock) &&
+        ip4_addr_cmp(&socket_ipv4_multicast_memberships[i].if_addr, if_addr) &&
+        ip4_addr_cmp(&socket_ipv4_multicast_memberships[i].multi_addr, multi_addr)) {
+      socket_ipv4_multicast_memberships[i].sock = NULL;
+      ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].if_addr);
+      ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].multi_addr);
+      return;
+    }
+  }
+}
+
+/** Drop all memberships of a socket that were not dropped explicitly via setsockopt.
+ *
+ * ATTENTION: this function is NOT called from tcpip_thread (or under CORE_LOCK).
+ */
+static void
+lwip_socket_drop_registered_memberships(int s)
+{
+  struct lwip_sock *sock = get_socket(s);
+  int i;
+
+  if (!sock) {
+    return;
+  }
+
+  for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
+    if (socket_ipv4_multicast_memberships[i].sock == sock) {
+      ip_addr_t multi_addr, if_addr;
+      ip_addr_copy_from_ip4(multi_addr, socket_ipv4_multicast_memberships[i].multi_addr);
+      ip_addr_copy_from_ip4(if_addr, socket_ipv4_multicast_memberships[i].if_addr);
+      socket_ipv4_multicast_memberships[i].sock = NULL;
+      ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].if_addr);
+      ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].multi_addr);
+
+      netconn_join_leave_group(sock->conn, &multi_addr, &if_addr, NETCONN_LEAVE);
+    }
+  }
+}
+#endif /* LWIP_IGMP */
+#endif /* LWIP_SOCKET */

+ 518 - 658
libs/thirdparty/lwip_2.1.2/src/api/tcpip.c → libs/thirdparty/LwIP/src/api/tcpip.c

@@ -1,658 +1,518 @@
-/**
- * @file
- * Sequential API Main thread module
- *
- */
-
-/*
- * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- *
- * Author: Adam Dunkels <adam@sics.se>
- *
- */
-
-#include "lwip/opt.h"
-
-#if !NO_SYS /* don't build if not configured for use in lwipopts.h */
-
-#include "lwip/priv/tcpip_priv.h"
-#include "lwip/sys.h"
-#include "lwip/memp.h"
-#include "lwip/mem.h"
-#include "lwip/init.h"
-#include "lwip/ip.h"
-#include "lwip/pbuf.h"
-#include "lwip/etharp.h"
-#include "netif/ethernet.h"
-
-#define TCPIP_MSG_VAR_REF(name)     API_VAR_REF(name)
-#define TCPIP_MSG_VAR_DECLARE(name) API_VAR_DECLARE(struct tcpip_msg, name)
-#define TCPIP_MSG_VAR_ALLOC(name)   API_VAR_ALLOC(struct tcpip_msg, MEMP_TCPIP_MSG_API, name, ERR_MEM)
-#define TCPIP_MSG_VAR_FREE(name)    API_VAR_FREE(MEMP_TCPIP_MSG_API, name)
-
-/* global variables */
-static tcpip_init_done_fn tcpip_init_done;
-static void *tcpip_init_done_arg;
-static sys_mbox_t tcpip_mbox;
-
-#if LWIP_TCPIP_CORE_LOCKING
-/** The global semaphore to lock the stack. */
-sys_mutex_t lock_tcpip_core;
-#endif /* LWIP_TCPIP_CORE_LOCKING */
-
-static void tcpip_thread_handle_msg(struct tcpip_msg *msg);
-
-#if !LWIP_TIMERS
-/* wait for a message with timers disabled (e.g. pass a timer-check trigger into tcpip_thread) */
-#define TCPIP_MBOX_FETCH(mbox, msg) sys_mbox_fetch(mbox, msg)
-#else /* !LWIP_TIMERS */
-/* wait for a message, timeouts are processed while waiting */
-#define TCPIP_MBOX_FETCH(mbox, msg) tcpip_timeouts_mbox_fetch(mbox, msg)
-/**
- * Wait (forever) for a message to arrive in an mbox.
- * While waiting, timeouts are processed.
- *
- * @param mbox the mbox to fetch the message from
- * @param msg the place to store the message
- */
-static void
-tcpip_timeouts_mbox_fetch(sys_mbox_t *mbox, void **msg)
-{
-  u32_t sleeptime, res;
-
-again:
-  LWIP_ASSERT_CORE_LOCKED();
-
-  sleeptime = sys_timeouts_sleeptime();
-  if (sleeptime == SYS_TIMEOUTS_SLEEPTIME_INFINITE) {
-    UNLOCK_TCPIP_CORE();
-    sys_arch_mbox_fetch(mbox, msg, 0);
-    LOCK_TCPIP_CORE();
-    return;
-  } else if (sleeptime == 0) {
-    sys_check_timeouts();
-    /* We try again to fetch a message from the mbox. */
-    goto again;
-  }
-
-  UNLOCK_TCPIP_CORE();
-  res = sys_arch_mbox_fetch(mbox, msg, sleeptime);
-  LOCK_TCPIP_CORE();
-  if (res == SYS_ARCH_TIMEOUT) {
-    /* If a SYS_ARCH_TIMEOUT value is returned, a timeout occurred
-       before a message could be fetched. */
-    sys_check_timeouts();
-    /* We try again to fetch a message from the mbox. */
-    goto again;
-  }
-}
-#endif /* !LWIP_TIMERS */
-
-/**
- * The main lwIP thread. This thread has exclusive access to lwIP core functions
- * (unless access to them is not locked). Other threads communicate with this
- * thread using message boxes.
- *
- * It also starts all the timers to make sure they are running in the right
- * thread context.
- *
- * @param arg unused argument
- */
-static void
-tcpip_thread(void *arg)
-{
-  struct tcpip_msg *msg;
-  LWIP_UNUSED_ARG(arg);
-
-  LWIP_MARK_TCPIP_THREAD();
-
-  LOCK_TCPIP_CORE();
-  if (tcpip_init_done != NULL) {
-    tcpip_init_done(tcpip_init_done_arg);
-  }
-
-  while (1) {                          /* MAIN Loop */
-    LWIP_TCPIP_THREAD_ALIVE();
-    /* wait for a message, timeouts are processed while waiting */
-    TCPIP_MBOX_FETCH(&tcpip_mbox, (void **)&msg);
-    if (msg == NULL) {
-      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: invalid message: NULL\n"));
-      LWIP_ASSERT("tcpip_thread: invalid message", 0);
-      continue;
-    }
-    tcpip_thread_handle_msg(msg);
-  }
-}
-
-/* Handle a single tcpip_msg
- * This is in its own function for access by tests only.
- */
-static void
-tcpip_thread_handle_msg(struct tcpip_msg *msg)
-{
-  switch (msg->type) {
-#if !LWIP_TCPIP_CORE_LOCKING
-    case TCPIP_MSG_API:
-      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API message %p\n", (void *)msg));
-      msg->msg.api_msg.function(msg->msg.api_msg.msg);
-      break;
-    case TCPIP_MSG_API_CALL:
-      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API CALL message %p\n", (void *)msg));
-      msg->msg.api_call.arg->err = msg->msg.api_call.function(msg->msg.api_call.arg);
-      sys_sem_signal(msg->msg.api_call.sem);
-      break;
-#endif /* !LWIP_TCPIP_CORE_LOCKING */
-
-#if !LWIP_TCPIP_CORE_LOCKING_INPUT
-    case TCPIP_MSG_INPKT:
-      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: PACKET %p\n", (void *)msg));
-      if (msg->msg.inp.input_fn(msg->msg.inp.p, msg->msg.inp.netif) != ERR_OK) {
-        pbuf_free(msg->msg.inp.p);
-      }
-      memp_free(MEMP_TCPIP_MSG_INPKT, msg);
-      break;
-#endif /* !LWIP_TCPIP_CORE_LOCKING_INPUT */
-
-#if LWIP_TCPIP_TIMEOUT && LWIP_TIMERS
-    case TCPIP_MSG_TIMEOUT:
-      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: TIMEOUT %p\n", (void *)msg));
-      sys_timeout(msg->msg.tmo.msecs, msg->msg.tmo.h, msg->msg.tmo.arg);
-      memp_free(MEMP_TCPIP_MSG_API, msg);
-      break;
-    case TCPIP_MSG_UNTIMEOUT:
-      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: UNTIMEOUT %p\n", (void *)msg));
-      sys_untimeout(msg->msg.tmo.h, msg->msg.tmo.arg);
-      memp_free(MEMP_TCPIP_MSG_API, msg);
-      break;
-#endif /* LWIP_TCPIP_TIMEOUT && LWIP_TIMERS */
-
-    case TCPIP_MSG_CALLBACK:
-      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK %p\n", (void *)msg));
-      msg->msg.cb.function(msg->msg.cb.ctx);
-      memp_free(MEMP_TCPIP_MSG_API, msg);
-      break;
-
-    case TCPIP_MSG_CALLBACK_STATIC:
-      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK_STATIC %p\n", (void *)msg));
-      msg->msg.cb.function(msg->msg.cb.ctx);
-      break;
-
-    default:
-      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: invalid message: %d\n", msg->type));
-      LWIP_ASSERT("tcpip_thread: invalid message", 0);
-      break;
-  }
-}
-
-#ifdef TCPIP_THREAD_TEST
-/** Work on queued items in single-threaded test mode */
-int
-tcpip_thread_poll_one(void)
-{
-  int ret = 0;
-  struct tcpip_msg *msg;
-
-  if (sys_arch_mbox_tryfetch(&tcpip_mbox, (void **)&msg) != SYS_ARCH_TIMEOUT) {
-    LOCK_TCPIP_CORE();
-    if (msg != NULL) {
-      tcpip_thread_handle_msg(msg);
-      ret = 1;
-    }
-    UNLOCK_TCPIP_CORE();
-  }
-  return ret;
-}
-#endif
-
-/**
- * Pass a received packet to tcpip_thread for input processing
- *
- * @param p the received packet
- * @param inp the network interface on which the packet was received
- * @param input_fn input function to call
- */
-err_t
-tcpip_inpkt(struct pbuf *p, struct netif *inp, netif_input_fn input_fn)
-{
-#if LWIP_TCPIP_CORE_LOCKING_INPUT
-  err_t ret;
-  LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_inpkt: PACKET %p/%p\n", (void *)p, (void *)inp));
-  LOCK_TCPIP_CORE();
-  ret = input_fn(p, inp);
-  UNLOCK_TCPIP_CORE();
-  return ret;
-#else /* LWIP_TCPIP_CORE_LOCKING_INPUT */
-  struct tcpip_msg *msg;
-
-  LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(tcpip_mbox));
-
-  msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_INPKT);
-  if (msg == NULL) {
-    return ERR_MEM;
-  }
-
-  msg->type = TCPIP_MSG_INPKT;
-  msg->msg.inp.p = p;
-  msg->msg.inp.netif = inp;
-  msg->msg.inp.input_fn = input_fn;
-  if (sys_mbox_trypost(&tcpip_mbox, msg) != ERR_OK) {
-    memp_free(MEMP_TCPIP_MSG_INPKT, msg);
-    return ERR_MEM;
-  }
-  return ERR_OK;
-#endif /* LWIP_TCPIP_CORE_LOCKING_INPUT */
-}
-
-/**
- * @ingroup lwip_os
- * Pass a received packet to tcpip_thread for input processing with
- * ethernet_input or ip_input. Don't call directly, pass to netif_add()
- * and call netif->input().
- *
- * @param p the received packet, p->payload pointing to the Ethernet header or
- *          to an IP header (if inp doesn't have NETIF_FLAG_ETHARP or
- *          NETIF_FLAG_ETHERNET flags)
- * @param inp the network interface on which the packet was received
- */
-err_t
-tcpip_input(struct pbuf *p, struct netif *inp)
-{
-#if LWIP_ETHERNET
-  if (inp->flags & (NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET)) {
-    return tcpip_inpkt(p, inp, ethernet_input);
-  } else
-#endif /* LWIP_ETHERNET */
-    return tcpip_inpkt(p, inp, ip_input);
-}
-
-/**
- * @ingroup lwip_os
- * Call a specific function in the thread context of
- * tcpip_thread for easy access synchronization.
- * A function called in that way may access lwIP core code
- * without fearing concurrent access.
- * Blocks until the request is posted.
- * Must not be called from interrupt context!
- *
- * @param function the function to call
- * @param ctx parameter passed to f
- * @return ERR_OK if the function was called, another err_t if not
- *
- * @see tcpip_try_callback
- */
-err_t
-tcpip_callback(tcpip_callback_fn function, void *ctx)
-{
-  struct tcpip_msg *msg;
-
-  LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(tcpip_mbox));
-
-  msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
-  if (msg == NULL) {
-    return ERR_MEM;
-  }
-
-  msg->type = TCPIP_MSG_CALLBACK;
-  msg->msg.cb.function = function;
-  msg->msg.cb.ctx = ctx;
-
-  sys_mbox_post(&tcpip_mbox, msg);
-  return ERR_OK;
-}
-
-/**
- * @ingroup lwip_os
- * Call a specific function in the thread context of
- * tcpip_thread for easy access synchronization.
- * A function called in that way may access lwIP core code
- * without fearing concurrent access.
- * Does NOT block when the request cannot be posted because the
- * tcpip_mbox is full, but returns ERR_MEM instead.
- * Can be called from interrupt context.
- *
- * @param function the function to call
- * @param ctx parameter passed to f
- * @return ERR_OK if the function was called, another err_t if not
- *
- * @see tcpip_callback
- */
-err_t
-tcpip_try_callback(tcpip_callback_fn function, void *ctx)
-{
-  struct tcpip_msg *msg;
-
-  LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(tcpip_mbox));
-
-  msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
-  if (msg == NULL) {
-    return ERR_MEM;
-  }
-
-  msg->type = TCPIP_MSG_CALLBACK;
-  msg->msg.cb.function = function;
-  msg->msg.cb.ctx = ctx;
-
-  if (sys_mbox_trypost(&tcpip_mbox, msg) != ERR_OK) {
-    memp_free(MEMP_TCPIP_MSG_API, msg);
-    return ERR_MEM;
-  }
-  return ERR_OK;
-}
-
-#if LWIP_TCPIP_TIMEOUT && LWIP_TIMERS
-/**
- * call sys_timeout in tcpip_thread
- *
- * @param msecs time in milliseconds for timeout
- * @param h function to be called on timeout
- * @param arg argument to pass to timeout function h
- * @return ERR_MEM on memory error, ERR_OK otherwise
- */
-err_t
-tcpip_timeout(u32_t msecs, sys_timeout_handler h, void *arg)
-{
-  struct tcpip_msg *msg;
-
-  LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(tcpip_mbox));
-
-  msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
-  if (msg == NULL) {
-    return ERR_MEM;
-  }
-
-  msg->type = TCPIP_MSG_TIMEOUT;
-  msg->msg.tmo.msecs = msecs;
-  msg->msg.tmo.h = h;
-  msg->msg.tmo.arg = arg;
-  sys_mbox_post(&tcpip_mbox, msg);
-  return ERR_OK;
-}
-
-/**
- * call sys_untimeout in tcpip_thread
- *
- * @param h function to be called on timeout
- * @param arg argument to pass to timeout function h
- * @return ERR_MEM on memory error, ERR_OK otherwise
- */
-err_t
-tcpip_untimeout(sys_timeout_handler h, void *arg)
-{
-  struct tcpip_msg *msg;
-
-  LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(tcpip_mbox));
-
-  msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
-  if (msg == NULL) {
-    return ERR_MEM;
-  }
-
-  msg->type = TCPIP_MSG_UNTIMEOUT;
-  msg->msg.tmo.h = h;
-  msg->msg.tmo.arg = arg;
-  sys_mbox_post(&tcpip_mbox, msg);
-  return ERR_OK;
-}
-#endif /* LWIP_TCPIP_TIMEOUT && LWIP_TIMERS */
-
-
-/**
- * Sends a message to TCPIP thread to call a function. Caller thread blocks on
- * on a provided semaphore, which ist NOT automatically signalled by TCPIP thread,
- * this has to be done by the user.
- * It is recommended to use LWIP_TCPIP_CORE_LOCKING since this is the way
- * with least runtime overhead.
- *
- * @param fn function to be called from TCPIP thread
- * @param apimsg argument to API function
- * @param sem semaphore to wait on
- * @return ERR_OK if the function was called, another err_t if not
- */
-err_t
-tcpip_send_msg_wait_sem(tcpip_callback_fn fn, void *apimsg, sys_sem_t *sem)
-{
-#if LWIP_TCPIP_CORE_LOCKING
-  LWIP_UNUSED_ARG(sem);
-  LOCK_TCPIP_CORE();
-  fn(apimsg);
-  UNLOCK_TCPIP_CORE();
-  return ERR_OK;
-#else /* LWIP_TCPIP_CORE_LOCKING */
-  TCPIP_MSG_VAR_DECLARE(msg);
-
-  LWIP_ASSERT("semaphore not initialized", sys_sem_valid(sem));
-  LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(tcpip_mbox));
-
-  TCPIP_MSG_VAR_ALLOC(msg);
-  TCPIP_MSG_VAR_REF(msg).type = TCPIP_MSG_API;
-  TCPIP_MSG_VAR_REF(msg).msg.api_msg.function = fn;
-  TCPIP_MSG_VAR_REF(msg).msg.api_msg.msg = apimsg;
-  sys_mbox_post(&tcpip_mbox, &TCPIP_MSG_VAR_REF(msg));
-  sys_arch_sem_wait(sem, 0);
-  TCPIP_MSG_VAR_FREE(msg);
-  return ERR_OK;
-#endif /* LWIP_TCPIP_CORE_LOCKING */
-}
-
-/**
- * Synchronously calls function in TCPIP thread and waits for its completion.
- * It is recommended to use LWIP_TCPIP_CORE_LOCKING (preferred) or
- * LWIP_NETCONN_SEM_PER_THREAD.
- * If not, a semaphore is created and destroyed on every call which is usually
- * an expensive/slow operation.
- * @param fn Function to call
- * @param call Call parameters
- * @return Return value from tcpip_api_call_fn
- */
-err_t
-tcpip_api_call(tcpip_api_call_fn fn, struct tcpip_api_call_data *call)
-{
-#if LWIP_TCPIP_CORE_LOCKING
-  err_t err;
-  LOCK_TCPIP_CORE();
-  err = fn(call);
-  UNLOCK_TCPIP_CORE();
-  return err;
-#else /* LWIP_TCPIP_CORE_LOCKING */
-  TCPIP_MSG_VAR_DECLARE(msg);
-
-#if !LWIP_NETCONN_SEM_PER_THREAD
-  err_t err = sys_sem_new(&call->sem, 0);
-  if (err != ERR_OK) {
-    return err;
-  }
-#endif /* LWIP_NETCONN_SEM_PER_THREAD */
-
-  LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(tcpip_mbox));
-
-  TCPIP_MSG_VAR_ALLOC(msg);
-  TCPIP_MSG_VAR_REF(msg).type = TCPIP_MSG_API_CALL;
-  TCPIP_MSG_VAR_REF(msg).msg.api_call.arg = call;
-  TCPIP_MSG_VAR_REF(msg).msg.api_call.function = fn;
-#if LWIP_NETCONN_SEM_PER_THREAD
-  TCPIP_MSG_VAR_REF(msg).msg.api_call.sem = LWIP_NETCONN_THREAD_SEM_GET();
-#else /* LWIP_NETCONN_SEM_PER_THREAD */
-  TCPIP_MSG_VAR_REF(msg).msg.api_call.sem = &call->sem;
-#endif /* LWIP_NETCONN_SEM_PER_THREAD */
-  sys_mbox_post(&tcpip_mbox, &TCPIP_MSG_VAR_REF(msg));
-  sys_arch_sem_wait(TCPIP_MSG_VAR_REF(msg).msg.api_call.sem, 0);
-  TCPIP_MSG_VAR_FREE(msg);
-
-#if !LWIP_NETCONN_SEM_PER_THREAD
-  sys_sem_free(&call->sem);
-#endif /* LWIP_NETCONN_SEM_PER_THREAD */
-
-  return call->err;
-#endif /* LWIP_TCPIP_CORE_LOCKING */
-}
-
-/**
- * @ingroup lwip_os
- * Allocate a structure for a static callback message and initialize it.
- * The message has a special type such that lwIP never frees it.
- * This is intended to be used to send "static" messages from interrupt context,
- * e.g. the message is allocated once and posted several times from an IRQ
- * using tcpip_callbackmsg_trycallback().
- * Example usage: Trigger execution of an ethernet IRQ DPC routine in lwIP thread context.
- *
- * @param function the function to call
- * @param ctx parameter passed to function
- * @return a struct pointer to pass to tcpip_callbackmsg_trycallback().
- *
- * @see tcpip_callbackmsg_trycallback()
- * @see tcpip_callbackmsg_delete()
- */
-struct tcpip_callback_msg *
-tcpip_callbackmsg_new(tcpip_callback_fn function, void *ctx)
-{
-  struct tcpip_msg *msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
-  if (msg == NULL) {
-    return NULL;
-  }
-  msg->type = TCPIP_MSG_CALLBACK_STATIC;
-  msg->msg.cb.function = function;
-  msg->msg.cb.ctx = ctx;
-  return (struct tcpip_callback_msg *)msg;
-}
-
-/**
- * @ingroup lwip_os
- * Free a callback message allocated by tcpip_callbackmsg_new().
- *
- * @param msg the message to free
- *
- * @see tcpip_callbackmsg_new()
- */
-void
-tcpip_callbackmsg_delete(struct tcpip_callback_msg *msg)
-{
-  memp_free(MEMP_TCPIP_MSG_API, msg);
-}
-
-/**
- * @ingroup lwip_os
- * Try to post a callback-message to the tcpip_thread tcpip_mbox.
- *
- * @param msg pointer to the message to post
- * @return sys_mbox_trypost() return code
- *
- * @see tcpip_callbackmsg_new()
- */
-err_t
-tcpip_callbackmsg_trycallback(struct tcpip_callback_msg *msg)
-{
-  LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(tcpip_mbox));
-  return sys_mbox_trypost(&tcpip_mbox, msg);
-}
-
-/**
- * @ingroup lwip_os
- * Try to post a callback-message to the tcpip_thread mbox.
- * Same as @ref tcpip_callbackmsg_trycallback but calls sys_mbox_trypost_fromisr(),
- * mainly to help FreeRTOS, where calls differ between task level and ISR level.
- *
- * @param msg pointer to the message to post
- * @return sys_mbox_trypost_fromisr() return code (without change, so this
- *         knowledge can be used to e.g. propagate "bool needs_scheduling")
- *
- * @see tcpip_callbackmsg_new()
- */
-err_t
-tcpip_callbackmsg_trycallback_fromisr(struct tcpip_callback_msg *msg)
-{
-  LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(tcpip_mbox));
-  return sys_mbox_trypost_fromisr(&tcpip_mbox, msg);
-}
-
-/**
- * @ingroup lwip_os
- * Initialize this module:
- * - initialize all sub modules
- * - start the tcpip_thread
- *
- * @param initfunc a function to call when tcpip_thread is running and finished initializing
- * @param arg argument to pass to initfunc
- */
-void
-tcpip_init(tcpip_init_done_fn initfunc, void *arg)
-{
-  lwip_init();
-
-  tcpip_init_done = initfunc;
-  tcpip_init_done_arg = arg;
-  if (sys_mbox_new(&tcpip_mbox, TCPIP_MBOX_SIZE) != ERR_OK) {
-    LWIP_ASSERT("failed to create tcpip_thread mbox", 0);
-  }
-#if LWIP_TCPIP_CORE_LOCKING
-  if (sys_mutex_new(&lock_tcpip_core) != ERR_OK) {
-    LWIP_ASSERT("failed to create lock_tcpip_core", 0);
-  }
-#endif /* LWIP_TCPIP_CORE_LOCKING */
-
-  sys_thread_new(TCPIP_THREAD_NAME, tcpip_thread, NULL, TCPIP_THREAD_STACKSIZE, TCPIP_THREAD_PRIO);
-}
-
-/**
- * Simple callback function used with tcpip_callback to free a pbuf
- * (pbuf_free has a wrong signature for tcpip_callback)
- *
- * @param p The pbuf (chain) to be dereferenced.
- */
-static void
-pbuf_free_int(void *p)
-{
-  struct pbuf *q = (struct pbuf *)p;
-  pbuf_free(q);
-}
-
-/**
- * A simple wrapper function that allows you to free a pbuf from interrupt context.
- *
- * @param p The pbuf (chain) to be dereferenced.
- * @return ERR_OK if callback could be enqueued, an err_t if not
- */
-err_t
-pbuf_free_callback(struct pbuf *p)
-{
-  return tcpip_try_callback(pbuf_free_int, p);
-}
-
-/**
- * A simple wrapper function that allows you to free heap memory from
- * interrupt context.
- *
- * @param m the heap memory to free
- * @return ERR_OK if callback could be enqueued, an err_t if not
- */
-err_t
-mem_free_callback(void *m)
-{
-  return tcpip_try_callback(mem_free, m);
-}
-
-#endif /* !NO_SYS */
+/**
+ * @file
+ * Sequential API Main thread module
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if !NO_SYS /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/priv/tcpip_priv.h"
+#include "lwip/sys.h"
+#include "lwip/memp.h"
+#include "lwip/mem.h"
+#include "lwip/init.h"
+#include "lwip/ip.h"
+#include "lwip/pbuf.h"
+#include "lwip/etharp.h"
+#include "netif/ethernet.h"
+
+#define TCPIP_MSG_VAR_REF(name)     API_VAR_REF(name)
+#define TCPIP_MSG_VAR_DECLARE(name) API_VAR_DECLARE(struct tcpip_msg, name)
+#define TCPIP_MSG_VAR_ALLOC(name)   API_VAR_ALLOC(struct tcpip_msg, MEMP_TCPIP_MSG_API, name, ERR_MEM)
+#define TCPIP_MSG_VAR_FREE(name)    API_VAR_FREE(MEMP_TCPIP_MSG_API, name)
+
+/* global variables */
+static tcpip_init_done_fn tcpip_init_done;
+static void *tcpip_init_done_arg;
+static sys_mbox_t mbox;
+
+#if LWIP_TCPIP_CORE_LOCKING
+/** The global semaphore to lock the stack. */
+sys_mutex_t lock_tcpip_core;
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+
+#if LWIP_TIMERS
+/* wait for a message, timeouts are processed while waiting */
+#define TCPIP_MBOX_FETCH(mbox, msg) sys_timeouts_mbox_fetch(mbox, msg)
+#else /* LWIP_TIMERS */
+/* wait for a message with timers disabled (e.g. pass a timer-check trigger into tcpip_thread) */
+#define TCPIP_MBOX_FETCH(mbox, msg) sys_mbox_fetch(mbox, msg)
+#endif /* LWIP_TIMERS */
+
+/**
+ * The main lwIP thread. This thread has exclusive access to lwIP core functions
+ * (unless access to them is not locked). Other threads communicate with this
+ * thread using message boxes.
+ *
+ * It also starts all the timers to make sure they are running in the right
+ * thread context.
+ *
+ * @param arg unused argument
+ */
+static void
+tcpip_thread(void *arg)
+{
+  struct tcpip_msg *msg;
+  LWIP_UNUSED_ARG(arg);
+
+  if (tcpip_init_done != NULL) {
+    tcpip_init_done(tcpip_init_done_arg);
+  }
+
+  LOCK_TCPIP_CORE();
+  while (1) {                          /* MAIN Loop */
+    UNLOCK_TCPIP_CORE();
+    LWIP_TCPIP_THREAD_ALIVE();
+    /* wait for a message, timeouts are processed while waiting */
+    TCPIP_MBOX_FETCH(&mbox, (void **)&msg);
+    LOCK_TCPIP_CORE();
+    if (msg == NULL) {
+      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: invalid message: NULL\n"));
+      LWIP_ASSERT("tcpip_thread: invalid message", 0);
+      continue;
+    }
+    switch (msg->type) {
+#if !LWIP_TCPIP_CORE_LOCKING
+    case TCPIP_MSG_API:
+      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API message %p\n", (void *)msg));
+      msg->msg.api_msg.function(msg->msg.api_msg.msg);
+      break;
+    case TCPIP_MSG_API_CALL:
+      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API CALL message %p\n", (void *)msg));
+      msg->msg.api_call.arg->err = msg->msg.api_call.function(msg->msg.api_call.arg);
+      sys_sem_signal(msg->msg.api_call.sem);
+      break;
+#endif /* !LWIP_TCPIP_CORE_LOCKING */
+
+#if !LWIP_TCPIP_CORE_LOCKING_INPUT
+    case TCPIP_MSG_INPKT:
+      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: PACKET %p\n", (void *)msg));
+      msg->msg.inp.input_fn(msg->msg.inp.p, msg->msg.inp.netif);
+      memp_free(MEMP_TCPIP_MSG_INPKT, msg);
+      break;
+#endif /* !LWIP_TCPIP_CORE_LOCKING_INPUT */
+
+#if LWIP_TCPIP_TIMEOUT && LWIP_TIMERS
+    case TCPIP_MSG_TIMEOUT:
+      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: TIMEOUT %p\n", (void *)msg));
+      sys_timeout(msg->msg.tmo.msecs, msg->msg.tmo.h, msg->msg.tmo.arg);
+      memp_free(MEMP_TCPIP_MSG_API, msg);
+      break;
+    case TCPIP_MSG_UNTIMEOUT:
+      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: UNTIMEOUT %p\n", (void *)msg));
+      sys_untimeout(msg->msg.tmo.h, msg->msg.tmo.arg);
+      memp_free(MEMP_TCPIP_MSG_API, msg);
+      break;
+#endif /* LWIP_TCPIP_TIMEOUT && LWIP_TIMERS */
+
+    case TCPIP_MSG_CALLBACK:
+      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK %p\n", (void *)msg));
+      msg->msg.cb.function(msg->msg.cb.ctx);
+      memp_free(MEMP_TCPIP_MSG_API, msg);
+      break;
+
+    case TCPIP_MSG_CALLBACK_STATIC:
+      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK_STATIC %p\n", (void *)msg));
+      msg->msg.cb.function(msg->msg.cb.ctx);
+      break;
+
+    default:
+      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: invalid message: %d\n", msg->type));
+      LWIP_ASSERT("tcpip_thread: invalid message", 0);
+      break;
+    }
+  }
+}
+
+/**
+ * Pass a received packet to tcpip_thread for input processing
+ *
+ * @param p the received packet
+ * @param inp the network interface on which the packet was received
+ * @param input_fn input function to call
+ */
+err_t
+tcpip_inpkt(struct pbuf *p, struct netif *inp, netif_input_fn input_fn)
+{
+#if LWIP_TCPIP_CORE_LOCKING_INPUT
+  err_t ret;
+  LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_inpkt: PACKET %p/%p\n", (void *)p, (void *)inp));
+  LOCK_TCPIP_CORE();
+  ret = input_fn(p, inp);
+  UNLOCK_TCPIP_CORE();
+  return ret;
+#else /* LWIP_TCPIP_CORE_LOCKING_INPUT */
+  struct tcpip_msg *msg;
+
+  LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(mbox));
+
+  msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_INPKT);
+  if (msg == NULL) {
+    return ERR_MEM;
+  }
+
+  msg->type = TCPIP_MSG_INPKT;
+  msg->msg.inp.p = p;
+  msg->msg.inp.netif = inp;
+  msg->msg.inp.input_fn = input_fn;
+  if (sys_mbox_trypost(&mbox, msg) != ERR_OK) {
+    memp_free(MEMP_TCPIP_MSG_INPKT, msg);
+    return ERR_MEM;
+  }
+  return ERR_OK;
+#endif /* LWIP_TCPIP_CORE_LOCKING_INPUT */
+}
+
+/**
+ * @ingroup lwip_os
+ * Pass a received packet to tcpip_thread for input processing with
+ * ethernet_input or ip_input. Don't call directly, pass to netif_add()
+ * and call netif->input().
+ *
+ * @param p the received packet, p->payload pointing to the Ethernet header or
+ *          to an IP header (if inp doesn't have NETIF_FLAG_ETHARP or
+ *          NETIF_FLAG_ETHERNET flags)
+ * @param inp the network interface on which the packet was received
+ */
+err_t
+tcpip_input(struct pbuf *p, struct netif *inp)
+{
+#if LWIP_ETHERNET
+  if (inp->flags & (NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET)) {
+    return tcpip_inpkt(p, inp, ethernet_input);
+  } else
+#endif /* LWIP_ETHERNET */
+  return tcpip_inpkt(p, inp, ip_input);
+}
+
+/**
+ * Call a specific function in the thread context of
+ * tcpip_thread for easy access synchronization.
+ * A function called in that way may access lwIP core code
+ * without fearing concurrent access.
+ *
+ * @param function the function to call
+ * @param ctx parameter passed to f
+ * @param block 1 to block until the request is posted, 0 to non-blocking mode
+ * @return ERR_OK if the function was called, another err_t if not
+ */
+err_t
+tcpip_callback_with_block(tcpip_callback_fn function, void *ctx, u8_t block)
+{
+  struct tcpip_msg *msg;
+
+  LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(mbox));
+
+  msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
+  if (msg == NULL) {
+    return ERR_MEM;
+  }
+
+  msg->type = TCPIP_MSG_CALLBACK;
+  msg->msg.cb.function = function;
+  msg->msg.cb.ctx = ctx;
+  if (block) {
+    sys_mbox_post(&mbox, msg);
+  } else {
+    if (sys_mbox_trypost(&mbox, msg) != ERR_OK) {
+      memp_free(MEMP_TCPIP_MSG_API, msg);
+      return ERR_MEM;
+    }
+  }
+  return ERR_OK;
+}
+
+#if LWIP_TCPIP_TIMEOUT && LWIP_TIMERS
+/**
+ * call sys_timeout in tcpip_thread
+ *
+ * @param msecs time in milliseconds for timeout
+ * @param h function to be called on timeout
+ * @param arg argument to pass to timeout function h
+ * @return ERR_MEM on memory error, ERR_OK otherwise
+ */
+err_t
+tcpip_timeout(u32_t msecs, sys_timeout_handler h, void *arg)
+{
+  struct tcpip_msg *msg;
+
+  LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(mbox));
+
+  msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
+  if (msg == NULL) {
+    return ERR_MEM;
+  }
+
+  msg->type = TCPIP_MSG_TIMEOUT;
+  msg->msg.tmo.msecs = msecs;
+  msg->msg.tmo.h = h;
+  msg->msg.tmo.arg = arg;
+  sys_mbox_post(&mbox, msg);
+  return ERR_OK;
+}
+
+/**
+ * call sys_untimeout in tcpip_thread
+ *
+ * @param h function to be called on timeout
+ * @param arg argument to pass to timeout function h
+ * @return ERR_MEM on memory error, ERR_OK otherwise
+ */
+err_t
+tcpip_untimeout(sys_timeout_handler h, void *arg)
+{
+  struct tcpip_msg *msg;
+
+  LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(mbox));
+
+  msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
+  if (msg == NULL) {
+    return ERR_MEM;
+  }
+
+  msg->type = TCPIP_MSG_UNTIMEOUT;
+  msg->msg.tmo.h = h;
+  msg->msg.tmo.arg = arg;
+  sys_mbox_post(&mbox, msg);
+  return ERR_OK;
+}
+#endif /* LWIP_TCPIP_TIMEOUT && LWIP_TIMERS */
+
+
+/**
+ * Sends a message to TCPIP thread to call a function. Caller thread blocks on
+ * on a provided semaphore, which ist NOT automatically signalled by TCPIP thread,
+ * this has to be done by the user.
+ * It is recommended to use LWIP_TCPIP_CORE_LOCKING since this is the way
+ * with least runtime overhead.
+ *
+ * @param fn function to be called from TCPIP thread
+ * @param apimsg argument to API function
+ * @param sem semaphore to wait on
+ * @return ERR_OK if the function was called, another err_t if not
+ */
+err_t
+tcpip_send_msg_wait_sem(tcpip_callback_fn fn, void *apimsg, sys_sem_t* sem)
+{
+#if LWIP_TCPIP_CORE_LOCKING
+  LWIP_UNUSED_ARG(sem);
+  LOCK_TCPIP_CORE();
+  fn(apimsg);
+  UNLOCK_TCPIP_CORE();
+  return ERR_OK;
+#else /* LWIP_TCPIP_CORE_LOCKING */
+  TCPIP_MSG_VAR_DECLARE(msg);
+
+  LWIP_ASSERT("semaphore not initialized", sys_sem_valid(sem));
+  LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(mbox));
+
+  TCPIP_MSG_VAR_ALLOC(msg);
+  TCPIP_MSG_VAR_REF(msg).type = TCPIP_MSG_API;
+  TCPIP_MSG_VAR_REF(msg).msg.api_msg.function = fn;
+  TCPIP_MSG_VAR_REF(msg).msg.api_msg.msg = apimsg;
+  sys_mbox_post(&mbox, &TCPIP_MSG_VAR_REF(msg));
+  sys_arch_sem_wait(sem, 0);
+  TCPIP_MSG_VAR_FREE(msg);
+  return ERR_OK;
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+}
+
+/**
+ * Synchronously calls function in TCPIP thread and waits for its completion.
+ * It is recommended to use LWIP_TCPIP_CORE_LOCKING (preferred) or
+ * LWIP_NETCONN_SEM_PER_THREAD. 
+ * If not, a semaphore is created and destroyed on every call which is usually
+ * an expensive/slow operation.
+ * @param fn Function to call
+ * @param call Call parameters
+ * @return Return value from tcpip_api_call_fn
+ */
+err_t
+tcpip_api_call(tcpip_api_call_fn fn, struct tcpip_api_call_data *call)
+{
+#if LWIP_TCPIP_CORE_LOCKING
+  err_t err;
+  LOCK_TCPIP_CORE();
+  err = fn(call);
+  UNLOCK_TCPIP_CORE();
+  return err;
+#else /* LWIP_TCPIP_CORE_LOCKING */
+  TCPIP_MSG_VAR_DECLARE(msg);
+
+#if !LWIP_NETCONN_SEM_PER_THREAD
+  err_t err = sys_sem_new(&call->sem, 0);
+  if (err != ERR_OK) {
+    return err;
+  }
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+  LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(mbox));
+
+  TCPIP_MSG_VAR_ALLOC(msg);
+  TCPIP_MSG_VAR_REF(msg).type = TCPIP_MSG_API_CALL;
+  TCPIP_MSG_VAR_REF(msg).msg.api_call.arg = call;
+  TCPIP_MSG_VAR_REF(msg).msg.api_call.function = fn;
+#if LWIP_NETCONN_SEM_PER_THREAD
+  TCPIP_MSG_VAR_REF(msg).msg.api_call.sem = LWIP_NETCONN_THREAD_SEM_GET();
+#else /* LWIP_NETCONN_SEM_PER_THREAD */
+  TCPIP_MSG_VAR_REF(msg).msg.api_call.sem = &call->sem;
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+  sys_mbox_post(&mbox, &TCPIP_MSG_VAR_REF(msg));
+  sys_arch_sem_wait(TCPIP_MSG_VAR_REF(msg).msg.api_call.sem, 0);
+  TCPIP_MSG_VAR_FREE(msg);
+
+#if !LWIP_NETCONN_SEM_PER_THREAD
+  sys_sem_free(&call->sem);
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+  return call->err;
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+}
+
+/**
+ * Allocate a structure for a static callback message and initialize it.
+ * This is intended to be used to send "static" messages from interrupt context.
+ *
+ * @param function the function to call
+ * @param ctx parameter passed to function
+ * @return a struct pointer to pass to tcpip_trycallback().
+ */
+struct tcpip_callback_msg*
+tcpip_callbackmsg_new(tcpip_callback_fn function, void *ctx)
+{
+  struct tcpip_msg *msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
+  if (msg == NULL) {
+    return NULL;
+  }
+  msg->type = TCPIP_MSG_CALLBACK_STATIC;
+  msg->msg.cb.function = function;
+  msg->msg.cb.ctx = ctx;
+  return (struct tcpip_callback_msg*)msg;
+}
+
+/**
+ * Free a callback message allocated by tcpip_callbackmsg_new().
+ *
+ * @param msg the message to free
+ */
+void
+tcpip_callbackmsg_delete(struct tcpip_callback_msg* msg)
+{
+  memp_free(MEMP_TCPIP_MSG_API, msg);
+}
+
+/**
+ * Try to post a callback-message to the tcpip_thread mbox
+ * This is intended to be used to send "static" messages from interrupt context.
+ *
+ * @param msg pointer to the message to post
+ * @return sys_mbox_trypost() return code
+ */
+err_t
+tcpip_trycallback(struct tcpip_callback_msg* msg)
+{
+  LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(mbox));
+  return sys_mbox_trypost(&mbox, msg);
+}
+
+/**
+ * @ingroup lwip_os
+ * Initialize this module:
+ * - initialize all sub modules
+ * - start the tcpip_thread
+ *
+ * @param initfunc a function to call when tcpip_thread is running and finished initializing
+ * @param arg argument to pass to initfunc
+ */
+void
+tcpip_init(tcpip_init_done_fn initfunc, void *arg)
+{
+  lwip_init();
+
+  tcpip_init_done = initfunc;
+  tcpip_init_done_arg = arg;
+  if (sys_mbox_new(&mbox, TCPIP_MBOX_SIZE) != ERR_OK) {
+    LWIP_ASSERT("failed to create tcpip_thread mbox", 0);
+  }
+#if LWIP_TCPIP_CORE_LOCKING
+  if (sys_mutex_new(&lock_tcpip_core) != ERR_OK) {
+    LWIP_ASSERT("failed to create lock_tcpip_core", 0);
+  }
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+
+  sys_thread_new(TCPIP_THREAD_NAME, tcpip_thread, NULL, TCPIP_THREAD_STACKSIZE, TCPIP_THREAD_PRIO);
+}
+
+/**
+ * Simple callback function used with tcpip_callback to free a pbuf
+ * (pbuf_free has a wrong signature for tcpip_callback)
+ *
+ * @param p The pbuf (chain) to be dereferenced.
+ */
+static void
+pbuf_free_int(void *p)
+{
+  struct pbuf *q = (struct pbuf *)p;
+  pbuf_free(q);
+}
+
+/**
+ * A simple wrapper function that allows you to free a pbuf from interrupt context.
+ *
+ * @param p The pbuf (chain) to be dereferenced.
+ * @return ERR_OK if callback could be enqueued, an err_t if not
+ */
+err_t
+pbuf_free_callback(struct pbuf *p)
+{
+  return tcpip_callback_with_block(pbuf_free_int, p, 0);
+}
+
+/**
+ * A simple wrapper function that allows you to free heap memory from
+ * interrupt context.
+ *
+ * @param m the heap memory to free
+ * @return ERR_OK if callback could be enqueued, an err_t if not
+ */
+err_t
+mem_free_callback(void *m)
+{
+  return tcpip_callback_with_block(mem_free, m, 0);
+}
+
+#endif /* !NO_SYS */

+ 179 - 174
libs/thirdparty/lwip_2.1.2/src/apps/http/fs.c → libs/thirdparty/LwIP/src/apps/httpd/fs.c

@@ -1,174 +1,179 @@
-/*
- * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- *
- * Author: Adam Dunkels <adam@sics.se>
- *
- */
-
-#include "lwip/apps/httpd_opts.h"
-#include "lwip/def.h"
-#include "lwip/apps/fs.h"
-#include <string.h>
-
-
-#include HTTPD_FSDATA_FILE
-
-/*-----------------------------------------------------------------------------------*/
-
-#if LWIP_HTTPD_CUSTOM_FILES
-int fs_open_custom(struct fs_file *file, const char *name);
-void fs_close_custom(struct fs_file *file);
-#if LWIP_HTTPD_FS_ASYNC_READ
-u8_t fs_canread_custom(struct fs_file *file);
-u8_t fs_wait_read_custom(struct fs_file *file, fs_wait_cb callback_fn, void *callback_arg);
-int fs_read_async_custom(struct fs_file *file, char *buffer, int count, fs_wait_cb callback_fn, void *callback_arg);
-#else /* LWIP_HTTPD_FS_ASYNC_READ */
-int fs_read_custom(struct fs_file *file, char *buffer, int count);
-#endif /* LWIP_HTTPD_FS_ASYNC_READ */
-#endif /* LWIP_HTTPD_CUSTOM_FILES */
-
-/*-----------------------------------------------------------------------------------*/
-err_t
-fs_open(struct fs_file *file, const char *name)
-{
-  const struct fsdata_file *f;
-
-  if ((file == NULL) || (name == NULL)) {
-    return ERR_ARG;
-  }
-
-#if LWIP_HTTPD_CUSTOM_FILES
-  if (fs_open_custom(file, name)) {
-    file->is_custom_file = 1;
-    return ERR_OK;
-  }
-  file->is_custom_file = 0;
-#endif /* LWIP_HTTPD_CUSTOM_FILES */
-
-  for (f = FS_ROOT; f != NULL; f = f->next) {
-    if (!strcmp(name, (const char *)f->name)) {
-      file->data = (const char *)f->data;
-      file->len = f->len;
-      file->index = f->len;
-      file->pextension = NULL;
-      file->flags = f->flags;
-#if HTTPD_PRECALCULATED_CHECKSUM
-      file->chksum_count = f->chksum_count;
-      file->chksum = f->chksum;
-#endif /* HTTPD_PRECALCULATED_CHECKSUM */
-#if LWIP_HTTPD_FILE_STATE
-      file->state = fs_state_init(file, name);
-#endif /* #if LWIP_HTTPD_FILE_STATE */
-      return ERR_OK;
-    }
-  }
-  /* file not found */
-  return ERR_VAL;
-}
-
-/*-----------------------------------------------------------------------------------*/
-void
-fs_close(struct fs_file *file)
-{
-#if LWIP_HTTPD_CUSTOM_FILES
-  if (file->is_custom_file) {
-    fs_close_custom(file);
-  }
-#endif /* LWIP_HTTPD_CUSTOM_FILES */
-#if LWIP_HTTPD_FILE_STATE
-  fs_state_free(file, file->state);
-#endif /* #if LWIP_HTTPD_FILE_STATE */
-  LWIP_UNUSED_ARG(file);
-}
-/*-----------------------------------------------------------------------------------*/
-#if LWIP_HTTPD_DYNAMIC_FILE_READ
-#if LWIP_HTTPD_FS_ASYNC_READ
-int
-fs_read_async(struct fs_file *file, char *buffer, int count, fs_wait_cb callback_fn, void *callback_arg)
-#else /* LWIP_HTTPD_FS_ASYNC_READ */
-int
-fs_read(struct fs_file *file, char *buffer, int count)
-#endif /* LWIP_HTTPD_FS_ASYNC_READ */
-{
-  int read;
-  if (file->index == file->len) {
-    return FS_READ_EOF;
-  }
-#if LWIP_HTTPD_FS_ASYNC_READ
-  LWIP_UNUSED_ARG(callback_fn);
-  LWIP_UNUSED_ARG(callback_arg);
-#endif /* LWIP_HTTPD_FS_ASYNC_READ */
-#if LWIP_HTTPD_CUSTOM_FILES
-  if (file->is_custom_file) {
-#if LWIP_HTTPD_FS_ASYNC_READ
-    return fs_read_async_custom(file, buffer, count, callback_fn, callback_arg);
-#else /* LWIP_HTTPD_FS_ASYNC_READ */
-    return fs_read_custom(file, buffer, count);
-#endif /* LWIP_HTTPD_FS_ASYNC_READ */
-  }
-#endif /* LWIP_HTTPD_CUSTOM_FILES */
-
-  read = file->len - file->index;
-  if (read > count) {
-    read = count;
-  }
-
-  MEMCPY(buffer, (file->data + file->index), read);
-  file->index += read;
-
-  return (read);
-}
-#endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */
-/*-----------------------------------------------------------------------------------*/
-#if LWIP_HTTPD_FS_ASYNC_READ
-int
-fs_is_file_ready(struct fs_file *file, fs_wait_cb callback_fn, void *callback_arg)
-{
-  if (file != NULL) {
-#if LWIP_HTTPD_FS_ASYNC_READ
-#if LWIP_HTTPD_CUSTOM_FILES
-    if (!fs_canread_custom(file)) {
-      if (fs_wait_read_custom(file, callback_fn, callback_arg)) {
-        return 0;
-      }
-    }
-#else /* LWIP_HTTPD_CUSTOM_FILES */
-    LWIP_UNUSED_ARG(callback_fn);
-    LWIP_UNUSED_ARG(callback_arg);
-#endif /* LWIP_HTTPD_CUSTOM_FILES */
-#endif /* LWIP_HTTPD_FS_ASYNC_READ */
-  }
-  return 1;
-}
-#endif /* LWIP_HTTPD_FS_ASYNC_READ */
-/*-----------------------------------------------------------------------------------*/
-int
-fs_bytes_left(struct fs_file *file)
-{
-  return file->len - file->index;
-}
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * All rights reserved. 
+ * 
+ * Redistribution and use in source and binary forms, with or without modification, 
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ * 
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/apps/httpd_opts.h"
+#include "lwip/def.h"
+#include "lwip/apps/fs.h"
+#include "fsdata.h"
+#include <string.h>
+
+
+#if HTTPD_USE_CUSTOM_FSDATA
+#include "fsdata_custom.c"
+#else /* HTTPD_USE_CUSTOM_FSDATA */
+#include "fsdata.c"
+#endif /* HTTPD_USE_CUSTOM_FSDATA */
+
+/*-----------------------------------------------------------------------------------*/
+
+#if LWIP_HTTPD_CUSTOM_FILES
+int fs_open_custom(struct fs_file *file, const char *name);
+void fs_close_custom(struct fs_file *file);
+#if LWIP_HTTPD_FS_ASYNC_READ
+u8_t fs_canread_custom(struct fs_file *file);
+u8_t fs_wait_read_custom(struct fs_file *file, fs_wait_cb callback_fn, void *callback_arg);
+int fs_read_async_custom(struct fs_file *file, char *buffer, int count, fs_wait_cb callback_fn, void *callback_arg);
+#else /* LWIP_HTTPD_FS_ASYNC_READ */
+int fs_read_custom(struct fs_file *file, char *buffer, int count);
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+#endif /* LWIP_HTTPD_CUSTOM_FILES */
+
+/*-----------------------------------------------------------------------------------*/
+err_t
+fs_open(struct fs_file *file, const char *name)
+{
+  const struct fsdata_file *f;
+
+  if ((file == NULL) || (name == NULL)) {
+     return ERR_ARG;
+  }
+
+#if LWIP_HTTPD_CUSTOM_FILES
+  if (fs_open_custom(file, name)) {
+    file->is_custom_file = 1;
+    return ERR_OK;
+  }
+  file->is_custom_file = 0;
+#endif /* LWIP_HTTPD_CUSTOM_FILES */
+
+  for (f = FS_ROOT; f != NULL; f = f->next) {
+    if (!strcmp(name, (const char *)f->name)) {
+      file->data = (const char *)f->data;
+      file->len = f->len;
+      file->index = f->len;
+      file->pextension = NULL;
+      file->flags = f->flags;
+#if HTTPD_PRECALCULATED_CHECKSUM
+      file->chksum_count = f->chksum_count;
+      file->chksum = f->chksum;
+#endif /* HTTPD_PRECALCULATED_CHECKSUM */
+#if LWIP_HTTPD_FILE_STATE
+      file->state = fs_state_init(file, name);
+#endif /* #if LWIP_HTTPD_FILE_STATE */
+      return ERR_OK;
+    }
+  }
+  /* file not found */
+  return ERR_VAL;
+}
+
+/*-----------------------------------------------------------------------------------*/
+void
+fs_close(struct fs_file *file)
+{
+#if LWIP_HTTPD_CUSTOM_FILES
+  if (file->is_custom_file) {
+    fs_close_custom(file);
+  }
+#endif /* LWIP_HTTPD_CUSTOM_FILES */
+#if LWIP_HTTPD_FILE_STATE
+  fs_state_free(file, file->state);
+#endif /* #if LWIP_HTTPD_FILE_STATE */
+  LWIP_UNUSED_ARG(file);
+}
+/*-----------------------------------------------------------------------------------*/
+#if LWIP_HTTPD_DYNAMIC_FILE_READ
+#if LWIP_HTTPD_FS_ASYNC_READ
+int
+fs_read_async(struct fs_file *file, char *buffer, int count, fs_wait_cb callback_fn, void *callback_arg)
+#else /* LWIP_HTTPD_FS_ASYNC_READ */
+int
+fs_read(struct fs_file *file, char *buffer, int count)
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+{
+  int read;
+  if(file->index == file->len) {
+    return FS_READ_EOF;
+  }
+#if LWIP_HTTPD_FS_ASYNC_READ
+  LWIP_UNUSED_ARG(callback_fn);
+  LWIP_UNUSED_ARG(callback_arg);
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+#if LWIP_HTTPD_CUSTOM_FILES
+  if (file->is_custom_file) {
+#if LWIP_HTTPD_FS_ASYNC_READ
+    return fs_read_async_custom(file, buffer, count, callback_fn, callback_arg);
+#else /* LWIP_HTTPD_FS_ASYNC_READ */
+    return fs_read_custom(file, buffer, count);
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+  }
+#endif /* LWIP_HTTPD_CUSTOM_FILES */
+
+  read = file->len - file->index;
+  if(read > count) {
+    read = count;
+  }
+
+  MEMCPY(buffer, (file->data + file->index), read);
+  file->index += read;
+
+  return(read);
+}
+#endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */
+/*-----------------------------------------------------------------------------------*/
+#if LWIP_HTTPD_FS_ASYNC_READ
+int
+fs_is_file_ready(struct fs_file *file, fs_wait_cb callback_fn, void *callback_arg)
+{
+  if (file != NULL) {
+#if LWIP_HTTPD_FS_ASYNC_READ
+#if LWIP_HTTPD_CUSTOM_FILES
+    if (!fs_canread_custom(file)) {
+      if (fs_wait_read_custom(file, callback_fn, callback_arg)) {
+        return 0;
+      }
+    }
+#else /* LWIP_HTTPD_CUSTOM_FILES */
+    LWIP_UNUSED_ARG(callback_fn);
+    LWIP_UNUSED_ARG(callback_arg);
+#endif /* LWIP_HTTPD_CUSTOM_FILES */
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+  }
+  return 1;
+}
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+/*-----------------------------------------------------------------------------------*/
+int
+fs_bytes_left(struct fs_file *file)
+{
+  return file->len - file->index;
+}

+ 21 - 21
libs/thirdparty/lwip_2.1.2/src/apps/http/fs/404.html → libs/thirdparty/LwIP/src/apps/httpd/fs/404.html

@@ -1,21 +1,21 @@
-<html>
-<head><title>lwIP - A Lightweight TCP/IP Stack</title></head>
-<body bgcolor="white" text="black">
-
-    <table width="100%">
-      <tr valign="top"><td width="80">	  
-	  <a href="http://www.sics.se/"><img src="/img/sics.gif"
-	  border="0" alt="SICS logo" title="SICS logo"></a>
-	</td><td width="500">	  
-	  <h1>lwIP - A Lightweight TCP/IP Stack</h1>
-	  <h2>404 - Page not found</h2>
-	  <p>
-	    Sorry, the page you are requesting was not found on this
-	    server. 
-	  </p>
-	</td><td>
-	  &nbsp;
-	</td></tr>
-      </table>
-</body>
-</html>
+<html>
+<head><title>lwIP - A Lightweight TCP/IP Stack</title></head>
+<body bgcolor="white" text="black">
+
+    <table width="100%">
+      <tr valign="top"><td width="80">	  
+	  <a href="http://www.sics.se/"><img src="/img/sics.gif"
+	  border="0" alt="SICS logo" title="SICS logo"></a>
+	</td><td width="500">	  
+	  <h1>lwIP - A Lightweight TCP/IP Stack</h1>
+	  <h2>404 - Page not found</h2>
+	  <p>
+	    Sorry, the page you are requesting was not found on this
+	    server. 
+	  </p>
+	</td><td>
+	  &nbsp;
+	</td></tr>
+      </table>
+</body>
+</html>

+ 0 - 0
libs/thirdparty/lwip_2.1.2/src/apps/http/fs/img/sics.gif → libs/thirdparty/LwIP/src/apps/httpd/fs/img/sics.gif


+ 47 - 47
libs/thirdparty/lwip_2.1.2/src/apps/http/fs/index.html → libs/thirdparty/LwIP/src/apps/httpd/fs/index.html

@@ -1,47 +1,47 @@
-<html>
-<head><title>lwIP - A Lightweight TCP/IP Stack</title></head>
-<body bgcolor="white" text="black">
-
-    <table width="100%">
-      <tr valign="top"><td width="80">	  
-	  <a href="http://www.sics.se/"><img src="/img/sics.gif"
-	  border="0" alt="SICS logo" title="SICS logo"></a>
-	</td><td width="500">	  
-	  <h1>lwIP - A Lightweight TCP/IP Stack</h1>
-	  <p>
-	    The web page you are watching was served by a simple web
-	    server running on top of the lightweight TCP/IP stack <a
-	    href="http://www.sics.se/~adam/lwip/">lwIP</a>.
-	  </p>
-	  <p>
-	    lwIP is an open source implementation of the TCP/IP
-	    protocol suite that was originally written by <a
-	    href="http://www.sics.se/~adam/lwip/">Adam Dunkels
-	    of the Swedish Institute of Computer Science</a> but now is
-	    being actively developed by a team of developers
-	    distributed world-wide. Since it's release, lwIP has
-	    spurred a lot of interest and has been ported to several
-	    platforms and operating systems. lwIP can be used either
-	    with or without an underlying OS.
-	  </p>
-	  <p>
-	    The focus of the lwIP TCP/IP implementation is to reduce
-	    the RAM usage while still having a full scale TCP. This
-	    makes lwIP suitable for use in embedded systems with tens
-	    of kilobytes of free RAM and room for around 40 kilobytes
-	    of code ROM.
-	  </p>
-	  <p>
-	    More information about lwIP can be found at the lwIP
-	    homepage at <a
-	    href="http://savannah.nongnu.org/projects/lwip/">http://savannah.nongnu.org/projects/lwip/</a>
-	    or at the lwIP wiki at <a
-	    href="http://lwip.wikia.com/">http://lwip.wikia.com/</a>.
-	  </p>
-	</td><td>
-	  &nbsp;
-	</td></tr>
-      </table>
-</body>
-</html>
-
+<html>
+<head><title>lwIP - A Lightweight TCP/IP Stack</title></head>
+<body bgcolor="white" text="black">
+
+    <table width="100%">
+      <tr valign="top"><td width="80">	  
+	  <a href="http://www.sics.se/"><img src="/img/sics.gif"
+	  border="0" alt="SICS logo" title="SICS logo"></a>
+	</td><td width="500">	  
+	  <h1>lwIP - A Lightweight TCP/IP Stack</h1>
+	  <p>
+	    The web page you are watching was served by a simple web
+	    server running on top of the lightweight TCP/IP stack <a
+	    href="http://www.sics.se/~adam/lwip/">lwIP</a>.
+	  </p>
+	  <p>
+	    lwIP is an open source implementation of the TCP/IP
+	    protocol suite that was originally written by <a
+	    href="http://www.sics.se/~adam/lwip/">Adam Dunkels
+	    of the Swedish Institute of Computer Science</a> but now is
+	    being actively developed by a team of developers
+	    distributed world-wide. Since it's release, lwIP has
+	    spurred a lot of interest and has been ported to several
+	    platforms and operating systems. lwIP can be used either
+	    with or without an underlying OS.
+	  </p>
+	  <p>
+	    The focus of the lwIP TCP/IP implementation is to reduce
+	    the RAM usage while still having a full scale TCP. This
+	    makes lwIP suitable for use in embedded systems with tens
+	    of kilobytes of free RAM and room for around 40 kilobytes
+	    of code ROM.
+	  </p>
+	  <p>
+	    More information about lwIP can be found at the lwIP
+	    homepage at <a
+	    href="http://savannah.nongnu.org/projects/lwip/">http://savannah.nongnu.org/projects/lwip/</a>
+	    or at the lwIP wiki at <a
+	    href="http://lwip.wikia.com/">http://lwip.wikia.com/</a>.
+	  </p>
+	</td><td>
+	  &nbsp;
+	</td></tr>
+      </table>
+</body>
+</html>
+

+ 50 - 41
libs/thirdparty/lwip_2.1.2/src/apps/http/fsdata.h → libs/thirdparty/LwIP/src/apps/httpd/fsdata.h

@@ -1,41 +1,50 @@
-/*
- * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- *
- * Author: Adam Dunkels <adam@sics.se>
- *
- */
-#ifndef LWIP_FSDATA_H
-#define LWIP_FSDATA_H
-
-#include "lwip/apps/httpd_opts.h"
-#include "lwip/apps/fs.h"
-
-/* THIS FILE IS DEPRECATED AND WILL BE REMOVED IN THE FUTURE */
-/* content was moved to fs.h to simplify #include structure */
-
-#endif /* LWIP_FSDATA_H */
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * All rights reserved. 
+ * 
+ * Redistribution and use in source and binary forms, with or without modification, 
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ * 
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef LWIP_FSDATA_H
+#define LWIP_FSDATA_H
+
+#include "lwip/apps/httpd_opts.h"
+#include "lwip/apps/fs.h"
+
+struct fsdata_file {
+  const struct fsdata_file *next;
+  const unsigned char *name;
+  const unsigned char *data;
+  int len;
+  u8_t flags;
+#if HTTPD_PRECALCULATED_CHECKSUM
+  u16_t chksum_count;
+  const struct fsdata_chksum *chksum;
+#endif /* HTTPD_PRECALCULATED_CHECKSUM */
+};
+
+#endif /* LWIP_FSDATA_H */

+ 2634 - 2746
libs/thirdparty/lwip_2.1.2/src/apps/http/tmp/httpd.c → libs/thirdparty/LwIP/src/apps/httpd/httpd.c

@@ -1,2746 +1,2634 @@
-/**
- * @file
- * LWIP HTTP server implementation
- */
-
-/*
- * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- *
- * Author: Adam Dunkels <adam@sics.se>
- *         Simon Goldschmidt
- *
- */
-
-/**
- * @defgroup httpd HTTP server
- * @ingroup apps
- *
- * This httpd supports for a
- * rudimentary server-side-include facility which will replace tags of the form
- * <!--#tag--> in any file whose extension is .shtml, .shtm or .ssi with
- * strings provided by an include handler whose pointer is provided to the
- * module via function http_set_ssi_handler().
- * Additionally, a simple common
- * gateway interface (CGI) handling mechanism has been added to allow clients
- * to hook functions to particular request URIs.
- *
- * To enable SSI support, define label LWIP_HTTPD_SSI in lwipopts.h.
- * To enable CGI support, define label LWIP_HTTPD_CGI in lwipopts.h.
- *
- * By default, the server assumes that HTTP headers are already present in
- * each file stored in the file system.  By defining LWIP_HTTPD_DYNAMIC_HEADERS in
- * lwipopts.h, this behavior can be changed such that the server inserts the
- * headers automatically based on the extension of the file being served.  If
- * this mode is used, be careful to ensure that the file system image used
- * does not already contain the header information.
- *
- * File system images without headers can be created using the makefsfile
- * tool with the -h command line option.
- *
- *
- * Notes about valid SSI tags
- * --------------------------
- *
- * The following assumptions are made about tags used in SSI markers:
- *
- * 1. No tag may contain '-' or whitespace characters within the tag name.
- * 2. Whitespace is allowed between the tag leadin "<!--#" and the start of
- *    the tag name and between the tag name and the leadout string "-->".
- * 3. The maximum tag name length is LWIP_HTTPD_MAX_TAG_NAME_LEN, currently 8 characters.
- *
- * Notes on CGI usage
- * ------------------
- *
- * The simple CGI support offered here works with GET method requests only
- * and can handle up to 16 parameters encoded into the URI. The handler
- * function may not write directly to the HTTP output but must return a
- * filename that the HTTP server will send to the browser as a response to
- * the incoming CGI request.
- *
- *
- *
- * The list of supported file types is quite short, so if makefsdata complains
- * about an unknown extension, make sure to add it (and its doctype) to
- * the 'g_psHTTPHeaders' list.
- */
-#include "lwip/init.h"
-#include "lwip/apps/httpd.h"
-#include "lwip/debug.h"
-#include "lwip/stats.h"
-#include "lwip/apps/fs.h"
-#include "httpd_structs.h"
-#include "lwip/def.h"
-
-#include "lwip/altcp.h"
-#include "lwip/altcp_tcp.h"
-#if HTTPD_ENABLE_HTTPS
-#include "lwip/altcp_tls.h"
-#endif
-#ifdef LWIP_HOOK_FILENAME
-#include LWIP_HOOK_FILENAME
-#endif
-#if LWIP_HTTPD_TIMING
-#include "lwip/sys.h"
-#endif /* LWIP_HTTPD_TIMING */
-
-#include <string.h> /* memset */
-#include <stdlib.h> /* atoi */
-#include <stdio.h>
-
-#if LWIP_TCP && LWIP_CALLBACK_API
-
-/** Minimum length for a valid HTTP/0.9 request: "GET /\r\n" -> 7 bytes */
-#define MIN_REQ_LEN   7
-
-#define CRLF "\r\n"
-#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
-#define HTTP11_CONNECTIONKEEPALIVE  "Connection: keep-alive"
-#define HTTP11_CONNECTIONKEEPALIVE2 "Connection: Keep-Alive"
-#endif
-
-#if LWIP_HTTPD_DYNAMIC_FILE_READ
-#define HTTP_IS_DYNAMIC_FILE(hs) ((hs)->buf != NULL)
-#else
-#define HTTP_IS_DYNAMIC_FILE(hs) 0
-#endif
-
-/* This defines checks whether tcp_write has to copy data or not */
-
-#ifndef HTTP_IS_DATA_VOLATILE
-/** tcp_write does not have to copy data when sent from rom-file-system directly */
-#define HTTP_IS_DATA_VOLATILE(hs)       (HTTP_IS_DYNAMIC_FILE(hs) ? TCP_WRITE_FLAG_COPY : 0)
-#endif
-/** Default: dynamic headers are sent from ROM (non-dynamic headers are handled like file data) */
-#ifndef HTTP_IS_HDR_VOLATILE
-#define HTTP_IS_HDR_VOLATILE(hs, ptr)   0
-#endif
-
-/* Return values for http_send_*() */
-#define HTTP_DATA_TO_SEND_FREED    3
-#define HTTP_DATA_TO_SEND_BREAK    2
-#define HTTP_DATA_TO_SEND_CONTINUE 1
-#define HTTP_NO_DATA_TO_SEND       0
-
-typedef struct {
-  const char *name;
-  u8_t shtml;
-} default_filename;
-
-static const default_filename httpd_default_filenames[] = {
-  {"/index.shtml", 1 },
-  {"/index.ssi",   1 },
-  {"/index.shtm",  1 },
-  {"/index.html",  0 },
-  {"/index.htm",   0 }
-};
-
-#define NUM_DEFAULT_FILENAMES LWIP_ARRAYSIZE(httpd_default_filenames)
-
-#if LWIP_HTTPD_SUPPORT_REQUESTLIST
-/** HTTP request is copied here from pbufs for simple parsing */
-static char httpd_req_buf[LWIP_HTTPD_MAX_REQ_LENGTH + 1];
-#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
-
-#if LWIP_HTTPD_SUPPORT_POST
-#if LWIP_HTTPD_POST_MAX_RESPONSE_URI_LEN > LWIP_HTTPD_MAX_REQUEST_URI_LEN
-#define LWIP_HTTPD_URI_BUF_LEN LWIP_HTTPD_POST_MAX_RESPONSE_URI_LEN
-#endif
-#endif
-#ifndef LWIP_HTTPD_URI_BUF_LEN
-#define LWIP_HTTPD_URI_BUF_LEN LWIP_HTTPD_MAX_REQUEST_URI_LEN
-#endif
-#if LWIP_HTTPD_URI_BUF_LEN
-/* Filename for response file to send when POST is finished or
- * search for default files when a directory is requested. */
-static char http_uri_buf[LWIP_HTTPD_URI_BUF_LEN + 1];
-#endif
-
-#if LWIP_HTTPD_DYNAMIC_HEADERS
-/* The number of individual strings that comprise the headers sent before each
- * requested file.
- */
-#define NUM_FILE_HDR_STRINGS 5
-#define HDR_STRINGS_IDX_HTTP_STATUS           0 /* e.g. "HTTP/1.0 200 OK\r\n" */
-#define HDR_STRINGS_IDX_SERVER_NAME           1 /* e.g. "Server: "HTTPD_SERVER_AGENT"\r\n" */
-#define HDR_STRINGS_IDX_CONTENT_LEN_KEEPALIVE 2 /* e.g. "Content-Length: xy\r\n" and/or "Connection: keep-alive\r\n" */
-#define HDR_STRINGS_IDX_CONTENT_LEN_NR        3 /* the byte count, when content-length is used */
-#define HDR_STRINGS_IDX_CONTENT_TYPE          4 /* the content type (or default answer content type including default document) */
-
-/* The dynamically generated Content-Length buffer needs space for CRLF + NULL */
-#define LWIP_HTTPD_MAX_CONTENT_LEN_OFFSET 3
-#ifndef LWIP_HTTPD_MAX_CONTENT_LEN_SIZE
-/* The dynamically generated Content-Length buffer shall be able to work with
-   ~953 MB (9 digits) */
-#define LWIP_HTTPD_MAX_CONTENT_LEN_SIZE   (9 + LWIP_HTTPD_MAX_CONTENT_LEN_OFFSET)
-#endif
-#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */
-
-#if LWIP_HTTPD_SSI
-
-#define HTTPD_LAST_TAG_PART 0xFFFF
-
-enum tag_check_state {
-  TAG_NONE,       /* Not processing an SSI tag */
-  TAG_LEADIN,     /* Tag lead in "<!--#" being processed */
-  TAG_FOUND,      /* Tag name being read, looking for lead-out start */
-  TAG_LEADOUT,    /* Tag lead out "-->" being processed */
-  TAG_SENDING     /* Sending tag replacement string */
-};
-
-struct http_ssi_state {
-  const char *parsed;     /* Pointer to the first unparsed byte in buf. */
-#if !LWIP_HTTPD_SSI_INCLUDE_TAG
-  const char *tag_started;/* Pointer to the first opening '<' of the tag. */
-#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG */
-  const char *tag_end;    /* Pointer to char after the closing '>' of the tag. */
-  u32_t parse_left; /* Number of unparsed bytes in buf. */
-  u16_t tag_index;   /* Counter used by tag parsing state machine */
-  u16_t tag_insert_len; /* Length of insert in string tag_insert */
-#if LWIP_HTTPD_SSI_MULTIPART
-  u16_t tag_part; /* Counter passed to and changed by tag insertion function to insert multiple times */
-#endif /* LWIP_HTTPD_SSI_MULTIPART */
-  u8_t tag_type; /* index into http_ssi_tag_desc array */
-  u8_t tag_name_len; /* Length of the tag name in string tag_name */
-  char tag_name[LWIP_HTTPD_MAX_TAG_NAME_LEN + 1]; /* Last tag name extracted */
-  char tag_insert[LWIP_HTTPD_MAX_TAG_INSERT_LEN + 1]; /* Insert string for tag_name */
-  enum tag_check_state tag_state; /* State of the tag processor */
-};
-
-struct http_ssi_tag_description {
-  const char *lead_in;
-  const char *lead_out;
-};
-
-#endif /* LWIP_HTTPD_SSI */
-
-struct http_state {
-#if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED
-  struct http_state *next;
-#endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */
-  struct fs_file file_handle;
-  struct fs_file *handle;
-  const char *file;       /* Pointer to first unsent byte in buf. */
-
-  struct altcp_pcb *pcb;
-#if LWIP_HTTPD_SUPPORT_REQUESTLIST
-  struct pbuf *req;
-#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
-
-#if LWIP_HTTPD_DYNAMIC_FILE_READ
-  char *buf;        /* File read buffer. */
-  int buf_len;      /* Size of file read buffer, buf. */
-#endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */
-  u32_t left;       /* Number of unsent bytes in buf. */
-  u8_t retries;
-#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
-  u8_t keepalive;
-#endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */
-#if LWIP_HTTPD_SSI
-  struct http_ssi_state *ssi;
-#endif /* LWIP_HTTPD_SSI */
-#if LWIP_HTTPD_CGI
-  char *params[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Params extracted from the request URI */
-  char *param_vals[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Values for each extracted param */
-#endif /* LWIP_HTTPD_CGI */
-#if LWIP_HTTPD_DYNAMIC_HEADERS
-  const char *hdrs[NUM_FILE_HDR_STRINGS]; /* HTTP headers to be sent. */
-  char hdr_content_len[LWIP_HTTPD_MAX_CONTENT_LEN_SIZE];
-  u16_t hdr_pos;     /* The position of the first unsent header byte in the
-                        current string */
-  u16_t hdr_index;   /* The index of the hdr string currently being sent. */
-#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */
-#if LWIP_HTTPD_TIMING
-  u32_t time_started;
-#endif /* LWIP_HTTPD_TIMING */
-#if LWIP_HTTPD_SUPPORT_POST
-  u32_t post_content_len_left;
-#if LWIP_HTTPD_POST_MANUAL_WND
-  u32_t unrecved_bytes;
-  u8_t no_auto_wnd;
-  u8_t post_finished;
-#endif /* LWIP_HTTPD_POST_MANUAL_WND */
-#endif /* LWIP_HTTPD_SUPPORT_POST*/
-};
-
-#if HTTPD_USE_MEM_POOL
-LWIP_MEMPOOL_DECLARE(HTTPD_STATE,     MEMP_NUM_PARALLEL_HTTPD_CONNS,     sizeof(struct http_state),     "HTTPD_STATE")
-#if LWIP_HTTPD_SSI
-LWIP_MEMPOOL_DECLARE(HTTPD_SSI_STATE, MEMP_NUM_PARALLEL_HTTPD_SSI_CONNS, sizeof(struct http_ssi_state), "HTTPD_SSI_STATE")
-#define HTTP_FREE_SSI_STATE(x)  LWIP_MEMPOOL_FREE(HTTPD_SSI_STATE, (x))
-#define HTTP_ALLOC_SSI_STATE()  (struct http_ssi_state *)LWIP_MEMPOOL_ALLOC(HTTPD_SSI_STATE)
-#endif /* LWIP_HTTPD_SSI */
-#define HTTP_ALLOC_HTTP_STATE() (struct http_state *)LWIP_MEMPOOL_ALLOC(HTTPD_STATE)
-#define HTTP_FREE_HTTP_STATE(x) LWIP_MEMPOOL_FREE(HTTPD_STATE, (x))
-#else /* HTTPD_USE_MEM_POOL */
-#define HTTP_ALLOC_HTTP_STATE() (struct http_state *)mem_malloc(sizeof(struct http_state))
-#define HTTP_FREE_HTTP_STATE(x) mem_free(x)
-#if LWIP_HTTPD_SSI
-#define HTTP_ALLOC_SSI_STATE()  (struct http_ssi_state *)mem_malloc(sizeof(struct http_ssi_state))
-#define HTTP_FREE_SSI_STATE(x)  mem_free(x)
-#endif /* LWIP_HTTPD_SSI */
-#endif /* HTTPD_USE_MEM_POOL */
-
-static err_t http_close_conn(struct altcp_pcb *pcb, struct http_state *hs);
-static err_t http_close_or_abort_conn(struct altcp_pcb *pcb, struct http_state *hs, u8_t abort_conn);
-static err_t http_find_file(struct http_state *hs, const char *uri, int is_09);
-static err_t http_init_file(struct http_state *hs, struct fs_file *file, int is_09, const char *uri, u8_t tag_check, char *params);
-static err_t http_poll(void *arg, struct altcp_pcb *pcb);
-static u8_t http_check_eof(struct altcp_pcb *pcb, struct http_state *hs);
-#if LWIP_HTTPD_FS_ASYNC_READ
-static void http_continue(void *connection);
-#endif /* LWIP_HTTPD_FS_ASYNC_READ */
-
-#if LWIP_HTTPD_SSI
-/* SSI insert handler function pointer. */
-static tSSIHandler httpd_ssi_handler;
-#if !LWIP_HTTPD_SSI_RAW
-static int httpd_num_tags;
-static const char **httpd_tags;
-#endif /* !LWIP_HTTPD_SSI_RAW */
-
-/* Define the available tag lead-ins and corresponding lead-outs.
- * ATTENTION: for the algorithm below using this array, it is essential
- * that the lead in differs in the first character! */
-const struct http_ssi_tag_description http_ssi_tag_desc[] = {
-  {"<!--#", "-->"},
-  {"/*#", "*/"}
-};
-
-#endif /* LWIP_HTTPD_SSI */
-
-#if LWIP_HTTPD_CGI
-/* CGI handler information */
-static const tCGI *httpd_cgis;
-static int httpd_num_cgis;
-static int http_cgi_paramcount;
-#define http_cgi_params     hs->params
-#define http_cgi_param_vals hs->param_vals
-#elif LWIP_HTTPD_CGI_SSI
-static char *http_cgi_params[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Params extracted from the request URI */
-static char *http_cgi_param_vals[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Values for each extracted param */
-#endif /* LWIP_HTTPD_CGI */
-
-#if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED
-/** global list of active HTTP connections, use to kill the oldest when
-    running out of memory */
-static struct http_state *http_connections;
-
-static void
-http_add_connection(struct http_state *hs)
-{
-  /* add the connection to the list */
-  hs->next = http_connections;
-  http_connections = hs;
-}
-
-static void
-http_remove_connection(struct http_state *hs)
-{
-  /* take the connection off the list */
-  if (http_connections) {
-    if (http_connections == hs) {
-      http_connections = hs->next;
-    } else {
-      struct http_state *last;
-      for (last = http_connections; last->next != NULL; last = last->next) {
-        if (last->next == hs) {
-          last->next = hs->next;
-          break;
-        }
-      }
-    }
-  }
-}
-
-static void
-http_kill_oldest_connection(u8_t ssi_required)
-{
-  struct http_state *hs = http_connections;
-  struct http_state *hs_free_next = NULL;
-  while (hs && hs->next) {
-#if LWIP_HTTPD_SSI
-    if (ssi_required) {
-      if (hs->next->ssi != NULL) {
-        hs_free_next = hs;
-      }
-    } else
-#else /* LWIP_HTTPD_SSI */
-    LWIP_UNUSED_ARG(ssi_required);
-#endif /* LWIP_HTTPD_SSI */
-    {
-      hs_free_next = hs;
-    }
-    LWIP_ASSERT("broken list", hs != hs->next);
-    hs = hs->next;
-  }
-  if (hs_free_next != NULL) {
-    LWIP_ASSERT("hs_free_next->next != NULL", hs_free_next->next != NULL);
-    LWIP_ASSERT("hs_free_next->next->pcb != NULL", hs_free_next->next->pcb != NULL);
-    /* send RST when killing a connection because of memory shortage */
-    http_close_or_abort_conn(hs_free_next->next->pcb, hs_free_next->next, 1); /* this also unlinks the http_state from the list */
-  }
-}
-#else /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */
-
-#define http_add_connection(hs)
-#define http_remove_connection(hs)
-
-#endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */
-
-#if LWIP_HTTPD_SSI
-/** Allocate as struct http_ssi_state. */
-static struct http_ssi_state *
-http_ssi_state_alloc(void)
-{
-  struct http_ssi_state *ret = HTTP_ALLOC_SSI_STATE();
-#if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED
-  if (ret == NULL) {
-    http_kill_oldest_connection(1);
-    ret = HTTP_ALLOC_SSI_STATE();
-  }
-#endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */
-  if (ret != NULL) {
-    memset(ret, 0, sizeof(struct http_ssi_state));
-  }
-  return ret;
-}
-
-/** Free a struct http_ssi_state. */
-static void
-http_ssi_state_free(struct http_ssi_state *ssi)
-{
-  if (ssi != NULL) {
-    HTTP_FREE_SSI_STATE(ssi);
-  }
-}
-#endif /* LWIP_HTTPD_SSI */
-
-/** Initialize a struct http_state.
- */
-static void
-http_state_init(struct http_state *hs)
-{
-  /* Initialize the structure. */
-  memset(hs, 0, sizeof(struct http_state));
-#if LWIP_HTTPD_DYNAMIC_HEADERS
-  /* Indicate that the headers are not yet valid */
-  hs->hdr_index = NUM_FILE_HDR_STRINGS;
-#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */
-}
-
-/** Allocate a struct http_state. */
-static struct http_state *
-http_state_alloc(void)
-{
-  struct http_state *ret = HTTP_ALLOC_HTTP_STATE();
-#if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED
-  if (ret == NULL) {
-    http_kill_oldest_connection(0);
-    ret = HTTP_ALLOC_HTTP_STATE();
-  }
-#endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */
-  if (ret != NULL) {
-    http_state_init(ret);
-    http_add_connection(ret);
-  }
-  return ret;
-}
-
-/** Free a struct http_state.
- * Also frees the file data if dynamic.
- */
-static void
-http_state_eof(struct http_state *hs)
-{
-  if (hs->handle) {
-#if LWIP_HTTPD_TIMING
-    u32_t ms_needed = sys_now() - hs->time_started;
-    u32_t needed = LWIP_MAX(1, (ms_needed / 100));
-    LWIP_DEBUGF(HTTPD_DEBUG_TIMING, ("httpd: needed %"U32_F" ms to send file of %d bytes -> %"U32_F" bytes/sec\n",
-                                     ms_needed, hs->handle->len, ((((u32_t)hs->handle->len) * 10) / needed)));
-#endif /* LWIP_HTTPD_TIMING */
-    fs_close(hs->handle);
-    hs->handle = NULL;
-  }
-#if LWIP_HTTPD_DYNAMIC_FILE_READ
-  if (hs->buf != NULL) {
-    mem_free(hs->buf);
-    hs->buf = NULL;
-  }
-#endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */
-#if LWIP_HTTPD_SSI
-  if (hs->ssi) {
-    http_ssi_state_free(hs->ssi);
-    hs->ssi = NULL;
-  }
-#endif /* LWIP_HTTPD_SSI */
-#if LWIP_HTTPD_SUPPORT_REQUESTLIST
-  if (hs->req) {
-    pbuf_free(hs->req);
-    hs->req = NULL;
-  }
-#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
-}
-
-/** Free a struct http_state.
- * Also frees the file data if dynamic.
- */
-static void
-http_state_free(struct http_state *hs)
-{
-  if (hs != NULL) {
-    http_state_eof(hs);
-    http_remove_connection(hs);
-    HTTP_FREE_HTTP_STATE(hs);
-  }
-}
-
-/** Call tcp_write() in a loop trying smaller and smaller length
- *
- * @param pcb altcp_pcb to send
- * @param ptr Data to send
- * @param length Length of data to send (in/out: on return, contains the
- *        amount of data sent)
- * @param apiflags directly passed to tcp_write
- * @return the return value of tcp_write
- */
-static err_t
-http_write(struct altcp_pcb *pcb, const void *ptr, u16_t *length, u8_t apiflags)
-{
-  u16_t len, max_len;
-  err_t err;
-  LWIP_ASSERT("length != NULL", length != NULL);
-  len = *length;
-  if (len == 0) {
-    return ERR_OK;
-  }
-  /* We cannot send more data than space available in the send buffer. */
-  max_len = altcp_sndbuf(pcb);
-  if (max_len < len) {
-    len = max_len;
-  }
-#ifdef HTTPD_MAX_WRITE_LEN
-  /* Additional limitation: e.g. don't enqueue more than 2*mss at once */
-  max_len = HTTPD_MAX_WRITE_LEN(pcb);
-  if (len > max_len) {
-    len = max_len;
-  }
-#endif /* HTTPD_MAX_WRITE_LEN */
-  do {
-    LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Trying to send %d bytes\n", len));
-    err = altcp_write(pcb, ptr, len, apiflags);
-    if (err == ERR_MEM) {
-      if ((altcp_sndbuf(pcb) == 0) ||
-          (altcp_sndqueuelen(pcb) >= TCP_SND_QUEUELEN)) {
-        /* no need to try smaller sizes */
-        len = 1;
-      } else {
-        len /= 2;
-      }
-      LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE,
-                  ("Send failed, trying less (%d bytes)\n", len));
-    }
-  } while ((err == ERR_MEM) && (len > 1));
-
-  if (err == ERR_OK) {
-    LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Sent %d bytes\n", len));
-    *length = len;
-  } else {
-    LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Send failed with err %d (\"%s\")\n", err, lwip_strerr(err)));
-    *length = 0;
-  }
-
-#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
-  /* ensure nagle is normally enabled (only disabled for persistent connections
-     when all data has been enqueued but the connection stays open for the next
-     request */
-  altcp_nagle_enable(pcb);
-#endif
-
-  return err;
-}
-
-/**
- * The connection shall be actively closed (using RST to close from fault states).
- * Reset the sent- and recv-callbacks.
- *
- * @param pcb the tcp pcb to reset callbacks
- * @param hs connection state to free
- */
-static err_t
-http_close_or_abort_conn(struct altcp_pcb *pcb, struct http_state *hs, u8_t abort_conn)
-{
-  err_t err;
-  LWIP_DEBUGF(HTTPD_DEBUG, ("Closing connection %p\n", (void *)pcb));
-
-#if LWIP_HTTPD_SUPPORT_POST
-  if (hs != NULL) {
-    if ((hs->post_content_len_left != 0)
-#if LWIP_HTTPD_POST_MANUAL_WND
-        || ((hs->no_auto_wnd != 0) && (hs->unrecved_bytes != 0))
-#endif /* LWIP_HTTPD_POST_MANUAL_WND */
-       ) {
-      /* make sure the post code knows that the connection is closed */
-      http_uri_buf[0] = 0;
-      httpd_post_finished(hs, http_uri_buf, LWIP_HTTPD_URI_BUF_LEN);
-    }
-  }
-#endif /* LWIP_HTTPD_SUPPORT_POST*/
-
-
-  altcp_arg(pcb, NULL);
-  altcp_recv(pcb, NULL);
-  altcp_err(pcb, NULL);
-  altcp_poll(pcb, NULL, 0);
-  altcp_sent(pcb, NULL);
-  if (hs != NULL) {
-    http_state_free(hs);
-  }
-
-  if (abort_conn) {
-    altcp_abort(pcb);
-    return ERR_OK;
-  }
-  err = altcp_close(pcb);
-  if (err != ERR_OK) {
-    LWIP_DEBUGF(HTTPD_DEBUG, ("Error %d closing %p\n", err, (void *)pcb));
-    /* error closing, try again later in poll */
-    altcp_poll(pcb, http_poll, HTTPD_POLL_INTERVAL);
-  }
-  return err;
-}
-
-/**
- * The connection shall be actively closed.
- * Reset the sent- and recv-callbacks.
- *
- * @param pcb the tcp pcb to reset callbacks
- * @param hs connection state to free
- */
-static err_t
-http_close_conn(struct altcp_pcb *pcb, struct http_state *hs)
-{
-  return http_close_or_abort_conn(pcb, hs, 0);
-}
-
-/** End of file: either close the connection (Connection: close) or
- * close the file (Connection: keep-alive)
- */
-static void
-http_eof(struct altcp_pcb *pcb, struct http_state *hs)
-{
-  /* HTTP/1.1 persistent connection? (Not supported for SSI) */
-#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
-  if (hs->keepalive) {
-    http_remove_connection(hs);
-
-    http_state_eof(hs);
-    http_state_init(hs);
-    /* restore state: */
-    hs->pcb = pcb;
-    hs->keepalive = 1;
-    http_add_connection(hs);
-    /* ensure nagle doesn't interfere with sending all data as fast as possible: */
-    altcp_nagle_disable(pcb);
-  } else
-#endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */
-  {
-    http_close_conn(pcb, hs);
-  }
-}
-
-#if LWIP_HTTPD_CGI || LWIP_HTTPD_CGI_SSI
-/**
- * Extract URI parameters from the parameter-part of an URI in the form
- * "test.cgi?x=y" @todo: better explanation!
- * Pointers to the parameters are stored in hs->param_vals.
- *
- * @param hs http connection state
- * @param params pointer to the NULL-terminated parameter string from the URI
- * @return number of parameters extracted
- */
-static int
-extract_uri_parameters(struct http_state *hs, char *params)
-{
-  char *pair;
-  char *equals;
-  int loop;
-
-  LWIP_UNUSED_ARG(hs);
-
-  /* If we have no parameters at all, return immediately. */
-  if (!params || (params[0] == '\0')) {
-    return (0);
-  }
-
-  /* Get a pointer to our first parameter */
-  pair = params;
-
-  /* Parse up to LWIP_HTTPD_MAX_CGI_PARAMETERS from the passed string and ignore the
-   * remainder (if any) */
-  for (loop = 0; (loop < LWIP_HTTPD_MAX_CGI_PARAMETERS) && pair; loop++) {
-
-    /* Save the name of the parameter */
-    http_cgi_params[loop] = pair;
-
-    /* Remember the start of this name=value pair */
-    equals = pair;
-
-    /* Find the start of the next name=value pair and replace the delimiter
-     * with a 0 to terminate the previous pair string. */
-    pair = strchr(pair, '&');
-    if (pair) {
-      *pair = '\0';
-      pair++;
-    } else {
-      /* We didn't find a new parameter so find the end of the URI and
-       * replace the space with a '\0' */
-      pair = strchr(equals, ' ');
-      if (pair) {
-        *pair = '\0';
-      }
-
-      /* Revert to NULL so that we exit the loop as expected. */
-      pair = NULL;
-    }
-
-    /* Now find the '=' in the previous pair, replace it with '\0' and save
-     * the parameter value string. */
-    equals = strchr(equals, '=');
-    if (equals) {
-      *equals = '\0';
-      http_cgi_param_vals[loop] = equals + 1;
-    } else {
-      http_cgi_param_vals[loop] = NULL;
-    }
-  }
-
-  return loop;
-}
-#endif /* LWIP_HTTPD_CGI || LWIP_HTTPD_CGI_SSI */
-
-#if LWIP_HTTPD_SSI
-/**
- * Insert a tag (found in an shtml in the form of "<!--#tagname-->" into the file.
- * The tag's name is stored in ssi->tag_name (NULL-terminated), the replacement
- * should be written to hs->tag_insert (up to a length of LWIP_HTTPD_MAX_TAG_INSERT_LEN).
- * The amount of data written is stored to ssi->tag_insert_len.
- *
- * @todo: return tag_insert_len - maybe it can be removed from struct http_state?
- *
- * @param hs http connection state
- */
-static void
-get_tag_insert(struct http_state *hs)
-{
-#if LWIP_HTTPD_SSI_RAW
-  const char *tag;
-#else /* LWIP_HTTPD_SSI_RAW */
-  int tag;
-#endif /* LWIP_HTTPD_SSI_RAW */
-  size_t len;
-  struct http_ssi_state *ssi;
-#if LWIP_HTTPD_SSI_MULTIPART
-  u16_t current_tag_part;
-#endif /* LWIP_HTTPD_SSI_MULTIPART */
-
-  LWIP_ASSERT("hs != NULL", hs != NULL);
-  ssi = hs->ssi;
-  LWIP_ASSERT("ssi != NULL", ssi != NULL);
-#if LWIP_HTTPD_SSI_MULTIPART
-  current_tag_part = ssi->tag_part;
-  ssi->tag_part = HTTPD_LAST_TAG_PART;
-#endif /* LWIP_HTTPD_SSI_MULTIPART */
-#if LWIP_HTTPD_SSI_RAW
-  tag = ssi->tag_name;
-#endif
-
-  if (httpd_ssi_handler
-#if !LWIP_HTTPD_SSI_RAW
-      && httpd_tags && httpd_num_tags
-#endif /* !LWIP_HTTPD_SSI_RAW */
-     ) {
-
-    /* Find this tag in the list we have been provided. */
-#if LWIP_HTTPD_SSI_RAW
-    {
-#else /* LWIP_HTTPD_SSI_RAW */
-    for (tag = 0; tag < httpd_num_tags; tag++) {
-      if (strcmp(ssi->tag_name, httpd_tags[tag]) == 0)
-#endif /* LWIP_HTTPD_SSI_RAW */
-      {
-        ssi->tag_insert_len = httpd_ssi_handler(tag, ssi->tag_insert,
-                                              LWIP_HTTPD_MAX_TAG_INSERT_LEN
-#if LWIP_HTTPD_SSI_MULTIPART
-                                              , current_tag_part, &ssi->tag_part
-#endif /* LWIP_HTTPD_SSI_MULTIPART */
-#if LWIP_HTTPD_FILE_STATE
-                                              , (hs->handle ? hs->handle->state : NULL)
-#endif /* LWIP_HTTPD_FILE_STATE */
-                                             );
-#if LWIP_HTTPD_SSI_RAW
-        if (ssi->tag_insert_len != HTTPD_SSI_TAG_UNKNOWN)
-#endif /* LWIP_HTTPD_SSI_RAW */
-        {
-          return;
-        }
-      }
-    }
-  }
-
-  /* If we drop out, we were asked to serve a page which contains tags that
-   * we don't have a handler for. Merely echo back the tags with an error
-   * marker. */
-#define UNKNOWN_TAG1_TEXT "<b>***UNKNOWN TAG "
-#define UNKNOWN_TAG1_LEN  18
-#define UNKNOWN_TAG2_TEXT "***</b>"
-#define UNKNOWN_TAG2_LEN  7
-  len = LWIP_MIN(sizeof(ssi->tag_name), LWIP_MIN(strlen(ssi->tag_name),
-                 LWIP_HTTPD_MAX_TAG_INSERT_LEN - (UNKNOWN_TAG1_LEN + UNKNOWN_TAG2_LEN)));
-  MEMCPY(ssi->tag_insert, UNKNOWN_TAG1_TEXT, UNKNOWN_TAG1_LEN);
-  MEMCPY(&ssi->tag_insert[UNKNOWN_TAG1_LEN], ssi->tag_name, len);
-  MEMCPY(&ssi->tag_insert[UNKNOWN_TAG1_LEN + len], UNKNOWN_TAG2_TEXT, UNKNOWN_TAG2_LEN);
-  ssi->tag_insert[UNKNOWN_TAG1_LEN + len + UNKNOWN_TAG2_LEN] = 0;
-
-  len = strlen(ssi->tag_insert);
-  LWIP_ASSERT("len <= 0xffff", len <= 0xffff);
-  ssi->tag_insert_len = (u16_t)len;
-}
-#endif /* LWIP_HTTPD_SSI */
-
-#if LWIP_HTTPD_DYNAMIC_HEADERS
-/**
- * Generate the relevant HTTP headers for the given filename and write
- * them into the supplied buffer.
- */
-static void
-get_http_headers(struct http_state *hs, const char *uri)
-{
-  size_t content_type;
-  char *tmp;
-  char *ext;
-  char *vars;
-
-  /* In all cases, the second header we send is the server identification
-     so set it here. */
-  hs->hdrs[HDR_STRINGS_IDX_SERVER_NAME] = g_psHTTPHeaderStrings[HTTP_HDR_SERVER];
-  hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEEPALIVE] = NULL;
-  hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_NR] = NULL;
-
-  /* Is this a normal file or the special case we use to send back the
-     default "404: Page not found" response? */
-  if (uri == NULL) {
-    hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_NOT_FOUND];
-#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
-    if (hs->keepalive) {
-      hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = g_psHTTPHeaderStrings[DEFAULT_404_HTML_PERSISTENT];
-    } else
-#endif
-    {
-      hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = g_psHTTPHeaderStrings[DEFAULT_404_HTML];
-    }
-
-    /* Set up to send the first header string. */
-    hs->hdr_index = 0;
-    hs->hdr_pos = 0;
-    return;
-  }
-  /* We are dealing with a particular filename. Look for one other
-      special case.  We assume that any filename with "404" in it must be
-      indicative of a 404 server error whereas all other files require
-      the 200 OK header. */
-  if (strstr(uri, "404")) {
-    hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_NOT_FOUND];
-  } else if (strstr(uri, "400")) {
-    hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_BAD_REQUEST];
-  } else if (strstr(uri, "501")) {
-    hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_NOT_IMPL];
-  } else {
-    hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_OK];
-  }
-
-  /* Determine if the URI has any variables and, if so, temporarily remove
-      them. */
-  vars = strchr(uri, '?');
-  if (vars) {
-    *vars = '\0';
-  }
-
-  /* Get a pointer to the file extension.  We find this by looking for the
-      last occurrence of "." in the filename passed. */
-  ext = NULL;
-  tmp = strchr(uri, '.');
-  while (tmp) {
-    ext = tmp + 1;
-    tmp = strchr(ext, '.');
-  }
-  if (ext != NULL) {
-    /* Now determine the content type and add the relevant header for that. */
-    for (content_type = 0; content_type < NUM_HTTP_HEADERS; content_type++) {
-      /* Have we found a matching extension? */
-      if (!lwip_stricmp(g_psHTTPHeaders[content_type].extension, ext)) {
-        break;
-      }
-    }
-  } else {
-    content_type = NUM_HTTP_HEADERS;
-  }
-
-  /* Reinstate the parameter marker if there was one in the original URI. */
-  if (vars) {
-    *vars = '?';
-  }
-
-#if LWIP_HTTPD_OMIT_HEADER_FOR_EXTENSIONLESS_URI
-  /* Does the URL passed have any file extension?  If not, we assume it
-     is a special-case URL used for control state notification and we do
-     not send any HTTP headers with the response. */
-  if (!ext) {
-    /* Force the header index to a value indicating that all headers
-       have already been sent. */
-    hs->hdr_index = NUM_FILE_HDR_STRINGS;
-    return;
-  }
-#endif /* LWIP_HTTPD_OMIT_HEADER_FOR_EXTENSIONLESS_URI */
-  /* Did we find a matching extension? */
-  if (content_type < NUM_HTTP_HEADERS) {
-    /* yes, store it */
-    hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = g_psHTTPHeaders[content_type].content_type;
-  } else if (!ext) {
-    /* no, no extension found -> use binary transfer to prevent the browser adding '.txt' on save */
-    hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = HTTP_HDR_APP;
-  } else {
-    /* No - use the default, plain text file type. */
-    hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = HTTP_HDR_DEFAULT_TYPE;
-  }
-  /* Set up to send the first header string. */
-  hs->hdr_index = 0;
-  hs->hdr_pos = 0;
-}
-
-/* Add content-length header? */
-static void
-get_http_content_length(struct http_state *hs)
-{
-  u8_t add_content_len = 0;
-
-  LWIP_ASSERT("already been here?", hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEEPALIVE] == NULL);
-
-  add_content_len = 0;
-#if LWIP_HTTPD_SSI
-  if (hs->ssi == NULL) /* @todo: get maximum file length from SSI */
-#endif /* LWIP_HTTPD_SSI */
-  {
-    if ((hs->handle != NULL) && (hs->handle->flags & FS_FILE_FLAGS_HEADER_PERSISTENT)) {
-      add_content_len = 1;
-    }
-  }
-  if (add_content_len) {
-    size_t len;
-    lwip_itoa(hs->hdr_content_len, (size_t)LWIP_HTTPD_MAX_CONTENT_LEN_SIZE,
-              hs->handle->len);
-    len = strlen(hs->hdr_content_len);
-    if (len <= LWIP_HTTPD_MAX_CONTENT_LEN_SIZE - LWIP_HTTPD_MAX_CONTENT_LEN_OFFSET) {
-      SMEMCPY(&hs->hdr_content_len[len], CRLF, 3);
-      hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_NR] = hs->hdr_content_len;
-    } else {
-      add_content_len = 0;
-    }
-  }
-#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
-  if (add_content_len) {
-    hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEEPALIVE] = g_psHTTPHeaderStrings[HTTP_HDR_KEEPALIVE_LEN];
-  } else {
-    hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEEPALIVE] = g_psHTTPHeaderStrings[HTTP_HDR_CONN_CLOSE];
-    hs->keepalive = 0;
-  }
-#else /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */
-  if (add_content_len) {
-    hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEEPALIVE] = g_psHTTPHeaderStrings[HTTP_HDR_CONTENT_LENGTH];
-  }
-#endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */
-}
-
-/** Sub-function of http_send(): send dynamic headers
- *
- * @returns: - HTTP_NO_DATA_TO_SEND: no new data has been enqueued
- *           - HTTP_DATA_TO_SEND_CONTINUE: continue with sending HTTP body
- *           - HTTP_DATA_TO_SEND_BREAK: data has been enqueued, headers pending,
- *                                      so don't send HTTP body yet
- *           - HTTP_DATA_TO_SEND_FREED: http_state and pcb are already freed
- */
-static u8_t
-http_send_headers(struct altcp_pcb *pcb, struct http_state *hs)
-{
-  err_t err;
-  u16_t len;
-  u8_t data_to_send = HTTP_NO_DATA_TO_SEND;
-  u16_t hdrlen, sendlen;
-
-  if (hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEEPALIVE] == NULL) {
-    /* set up "content-length" and "connection:" headers */
-    get_http_content_length(hs);
-  }
-
-  /* How much data can we send? */
-  len = altcp_sndbuf(pcb);
-  sendlen = len;
-
-  while (len && (hs->hdr_index < NUM_FILE_HDR_STRINGS) && sendlen) {
-    const void *ptr;
-    u16_t old_sendlen;
-    u8_t apiflags;
-    /* How much do we have to send from the current header? */
-    hdrlen = (u16_t)strlen(hs->hdrs[hs->hdr_index]);
-
-    /* How much of this can we send? */
-    sendlen = (len < (hdrlen - hs->hdr_pos)) ? len : (hdrlen - hs->hdr_pos);
-
-    /* Send this amount of data or as much as we can given memory
-     * constraints. */
-    ptr = (const void *)(hs->hdrs[hs->hdr_index] + hs->hdr_pos);
-    old_sendlen = sendlen;
-    apiflags = HTTP_IS_HDR_VOLATILE(hs, ptr);
-    if (hs->hdr_index == HDR_STRINGS_IDX_CONTENT_LEN_NR) {
-      /* content-length is always volatile */
-      apiflags |= TCP_WRITE_FLAG_COPY;
-    }
-    if (hs->hdr_index < NUM_FILE_HDR_STRINGS - 1) {
-      apiflags |= TCP_WRITE_FLAG_MORE;
-    }
-    err = http_write(pcb, ptr, &sendlen, apiflags);
-    if ((err == ERR_OK) && (old_sendlen != sendlen)) {
-      /* Remember that we added some more data to be transmitted. */
-      data_to_send = HTTP_DATA_TO_SEND_CONTINUE;
-    } else if (err != ERR_OK) {
-      /* special case: http_write does not try to send 1 byte */
-      sendlen = 0;
-    }
-
-    /* Fix up the header position for the next time round. */
-    hs->hdr_pos += sendlen;
-    len -= sendlen;
-
-    /* Have we finished sending this string? */
-    if (hs->hdr_pos == hdrlen) {
-      /* Yes - move on to the next one */
-      hs->hdr_index++;
-      /* skip headers that are NULL (not all headers are required) */
-      while ((hs->hdr_index < NUM_FILE_HDR_STRINGS) &&
-             (hs->hdrs[hs->hdr_index] == NULL)) {
-        hs->hdr_index++;
-      }
-      hs->hdr_pos = 0;
-    }
-  }
-
-  if ((hs->hdr_index >= NUM_FILE_HDR_STRINGS) && (hs->file == NULL)) {
-    /* When we are at the end of the headers, check for data to send
-     * instead of waiting for ACK from remote side to continue
-     * (which would happen when sending files from async read). */
-    if (http_check_eof(pcb, hs)) {
-      data_to_send = HTTP_DATA_TO_SEND_BREAK;
-    } else {
-      /* At this point, for non-keepalive connections, hs is deallocated an
-         pcb is closed. */
-      return HTTP_DATA_TO_SEND_FREED;
-    }
-  }
-  /* If we get here and there are still header bytes to send, we send
-   * the header information we just wrote immediately. If there are no
-   * more headers to send, but we do have file data to send, drop through
-   * to try to send some file data too. */
-  if ((hs->hdr_index < NUM_FILE_HDR_STRINGS) || !hs->file) {
-    LWIP_DEBUGF(HTTPD_DEBUG, ("tcp_output\n"));
-    return HTTP_DATA_TO_SEND_BREAK;
-  }
-  return data_to_send;
-}
-#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */
-
-/** Sub-function of http_send(): end-of-file (or block) is reached,
- * either close the file or read the next block (if supported).
- *
- * @returns: 0 if the file is finished or no data has been read
- *           1 if the file is not finished and data has been read
- */
-static u8_t
-http_check_eof(struct altcp_pcb *pcb, struct http_state *hs)
-{
-  int bytes_left;
-#if LWIP_HTTPD_DYNAMIC_FILE_READ
-  int count;
-#ifdef HTTPD_MAX_WRITE_LEN
-  int max_write_len;
-#endif /* HTTPD_MAX_WRITE_LEN */
-#endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */
-
-  /* Do we have a valid file handle? */
-  if (hs->handle == NULL) {
-    /* No - close the connection. */
-    http_eof(pcb, hs);
-    return 0;
-  }
-  bytes_left = fs_bytes_left(hs->handle);
-  if (bytes_left <= 0) {
-    /* We reached the end of the file so this request is done. */
-    LWIP_DEBUGF(HTTPD_DEBUG, ("End of file.\n"));
-    http_eof(pcb, hs);
-    return 0;
-  }
-#if LWIP_HTTPD_DYNAMIC_FILE_READ
-  /* Do we already have a send buffer allocated? */
-  if (hs->buf) {
-    /* Yes - get the length of the buffer */
-    count = LWIP_MIN(hs->buf_len, bytes_left);
-  } else {
-    /* We don't have a send buffer so allocate one now */
-    count = altcp_sndbuf(pcb);
-    if (bytes_left < count) {
-      count = bytes_left;
-    }
-#ifdef HTTPD_MAX_WRITE_LEN
-    /* Additional limitation: e.g. don't enqueue more than 2*mss at once */
-    max_write_len = HTTPD_MAX_WRITE_LEN(pcb);
-    if (count > max_write_len) {
-      count = max_write_len;
-    }
-#endif /* HTTPD_MAX_WRITE_LEN */
-    do {
-      hs->buf = (char *)mem_malloc((mem_size_t)count);
-      if (hs->buf != NULL) {
-        hs->buf_len = count;
-        break;
-      }
-      count = count / 2;
-    } while (count > 100);
-
-    /* Did we get a send buffer? If not, return immediately. */
-    if (hs->buf == NULL) {
-      LWIP_DEBUGF(HTTPD_DEBUG, ("No buff\n"));
-      return 0;
-    }
-  }
-
-  /* Read a block of data from the file. */
-  LWIP_DEBUGF(HTTPD_DEBUG, ("Trying to read %d bytes.\n", count));
-
-#if LWIP_HTTPD_FS_ASYNC_READ
-  count = fs_read_async(hs->handle, hs->buf, count, http_continue, hs);
-#else /* LWIP_HTTPD_FS_ASYNC_READ */
-  count = fs_read(hs->handle, hs->buf, count);
-#endif /* LWIP_HTTPD_FS_ASYNC_READ */
-  if (count < 0) {
-    if (count == FS_READ_DELAYED) {
-      /* Delayed read, wait for FS to unblock us */
-      return 0;
-    }
-    /* We reached the end of the file so this request is done.
-     * @todo: close here for HTTP/1.1 when reading file fails */
-    LWIP_DEBUGF(HTTPD_DEBUG, ("End of file.\n"));
-    http_eof(pcb, hs);
-    return 0;
-  }
-
-  /* Set up to send the block of data we just read */
-  LWIP_DEBUGF(HTTPD_DEBUG, ("Read %d bytes.\n", count));
-  hs->left = count;
-  hs->file = hs->buf;
-#if LWIP_HTTPD_SSI
-  if (hs->ssi) {
-    hs->ssi->parse_left = count;
-    hs->ssi->parsed = hs->buf;
-  }
-#endif /* LWIP_HTTPD_SSI */
-#else /* LWIP_HTTPD_DYNAMIC_FILE_READ */
-  LWIP_ASSERT("SSI and DYNAMIC_HEADERS turned off but eof not reached", 0);
-#endif /* LWIP_HTTPD_SSI || LWIP_HTTPD_DYNAMIC_HEADERS */
-  return 1;
-}
-
-/** Sub-function of http_send(): This is the normal send-routine for non-ssi files
- *
- * @returns: - 1: data has been written (so call tcp_ouput)
- *           - 0: no data has been written (no need to call tcp_output)
- */
-static u8_t
-http_send_data_nonssi(struct altcp_pcb *pcb, struct http_state *hs)
-{
-  err_t err;
-  u16_t len;
-  u8_t data_to_send = 0;
-
-  /* We are not processing an SHTML file so no tag checking is necessary.
-   * Just send the data as we received it from the file. */
-  len = (u16_t)LWIP_MIN(hs->left, 0xffff);
-
-  err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs));
-  if (err == ERR_OK) {
-    data_to_send = 1;
-    hs->file += len;
-    hs->left -= len;
-  }
-
-  return data_to_send;
-}
-
-#if LWIP_HTTPD_SSI
-/** Sub-function of http_send(): This is the send-routine for ssi files
- *
- * @returns: - 1: data has been written (so call tcp_ouput)
- *           - 0: no data has been written (no need to call tcp_output)
- */
-static u8_t
-http_send_data_ssi(struct altcp_pcb *pcb, struct http_state *hs)
-{
-  err_t err = ERR_OK;
-  u16_t len;
-  u8_t data_to_send = 0;
-  u8_t tag_type;
-
-  struct http_ssi_state *ssi = hs->ssi;
-  LWIP_ASSERT("ssi != NULL", ssi != NULL);
-  /* We are processing an SHTML file so need to scan for tags and replace
-   * them with insert strings. We need to be careful here since a tag may
-   * straddle the boundary of two blocks read from the file and we may also
-   * have to split the insert string between two tcp_write operations. */
-
-  /* How much data could we send? */
-  len = altcp_sndbuf(pcb);
-
-  /* Do we have remaining data to send before parsing more? */
-  if (ssi->parsed > hs->file) {
-    len = (u16_t)LWIP_MIN(ssi->parsed - hs->file, 0xffff);
-
-    err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs));
-    if (err == ERR_OK) {
-      data_to_send = 1;
-      hs->file += len;
-      hs->left -= len;
-    }
-
-    /* If the send buffer is full, return now. */
-    if (altcp_sndbuf(pcb) == 0) {
-      return data_to_send;
-    }
-  }
-
-  LWIP_DEBUGF(HTTPD_DEBUG, ("State %d, %d left\n", ssi->tag_state, (int)ssi->parse_left));
-
-  /* We have sent all the data that was already parsed so continue parsing
-   * the buffer contents looking for SSI tags. */
-  while (((ssi->tag_state == TAG_SENDING) || ssi->parse_left) && (err == ERR_OK)) {
-    if (len == 0) {
-      return data_to_send;
-    }
-    switch (ssi->tag_state) {
-      case TAG_NONE:
-        /* We are not currently processing an SSI tag so scan for the
-         * start of the lead-in marker. */
-        for (tag_type = 0; tag_type < LWIP_ARRAYSIZE(http_ssi_tag_desc); tag_type++) {
-          if (*ssi->parsed == http_ssi_tag_desc[tag_type].lead_in[0]) {
-            /* We found what could be the lead-in for a new tag so change
-             * state appropriately. */
-            ssi->tag_type = tag_type;
-            ssi->tag_state = TAG_LEADIN;
-            ssi->tag_index = 1;
-  #if !LWIP_HTTPD_SSI_INCLUDE_TAG
-            ssi->tag_started = ssi->parsed;
-  #endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG */
-            break;
-          }
-        }
-
-        /* Move on to the next character in the buffer */
-        ssi->parse_left--;
-        ssi->parsed++;
-        break;
-
-      case TAG_LEADIN:
-        /* We are processing the lead-in marker, looking for the start of
-         * the tag name. */
-
-        /* Have we reached the end of the leadin? */
-        if (http_ssi_tag_desc[ssi->tag_type].lead_in[ssi->tag_index] == 0) {
-          ssi->tag_index = 0;
-          ssi->tag_state = TAG_FOUND;
-        } else {
-          /* Have we found the next character we expect for the tag leadin? */
-          if (*ssi->parsed == http_ssi_tag_desc[ssi->tag_type].lead_in[ssi->tag_index]) {
-            /* Yes - move to the next one unless we have found the complete
-             * leadin, in which case we start looking for the tag itself */
-            ssi->tag_index++;
-          } else {
-            /* We found an unexpected character so this is not a tag. Move
-             * back to idle state. */
-            ssi->tag_state = TAG_NONE;
-          }
-
-          /* Move on to the next character in the buffer */
-          ssi->parse_left--;
-          ssi->parsed++;
-        }
-        break;
-
-      case TAG_FOUND:
-        /* We are reading the tag name, looking for the start of the
-         * lead-out marker and removing any whitespace found. */
-
-        /* Remove leading whitespace between the tag leading and the first
-         * tag name character. */
-        if ((ssi->tag_index == 0) && ((*ssi->parsed == ' ') ||
-                                      (*ssi->parsed == '\t') || (*ssi->parsed == '\n') ||
-                                      (*ssi->parsed == '\r'))) {
-          /* Move on to the next character in the buffer */
-          ssi->parse_left--;
-          ssi->parsed++;
-          break;
-        }
-
-        /* Have we found the end of the tag name? This is signalled by
-         * us finding the first leadout character or whitespace */
-        if ((*ssi->parsed == http_ssi_tag_desc[ssi->tag_type].lead_out[0]) ||
-            (*ssi->parsed == ' ')  || (*ssi->parsed == '\t') ||
-            (*ssi->parsed == '\n') || (*ssi->parsed == '\r')) {
-
-          if (ssi->tag_index == 0) {
-            /* We read a zero length tag so ignore it. */
-            ssi->tag_state = TAG_NONE;
-          } else {
-            /* We read a non-empty tag so go ahead and look for the
-             * leadout string. */
-            ssi->tag_state = TAG_LEADOUT;
-            LWIP_ASSERT("ssi->tag_index <= 0xff", ssi->tag_index <= 0xff);
-            ssi->tag_name_len = (u8_t)ssi->tag_index;
-            ssi->tag_name[ssi->tag_index] = '\0';
-            if (*ssi->parsed == http_ssi_tag_desc[ssi->tag_type].lead_out[0]) {
-              ssi->tag_index = 1;
-            } else {
-              ssi->tag_index = 0;
-            }
-          }
-        } else {
-          /* This character is part of the tag name so save it */
-          if (ssi->tag_index < LWIP_HTTPD_MAX_TAG_NAME_LEN) {
-            ssi->tag_name[ssi->tag_index++] = *ssi->parsed;
-          } else {
-            /* The tag was too long so ignore it. */
-            ssi->tag_state = TAG_NONE;
-          }
-        }
-
-        /* Move on to the next character in the buffer */
-        ssi->parse_left--;
-        ssi->parsed++;
-
-        break;
-
-      /* We are looking for the end of the lead-out marker. */
-      case TAG_LEADOUT:
-        /* Remove leading whitespace between the tag leading and the first
-         * tag leadout character. */
-        if ((ssi->tag_index == 0) && ((*ssi->parsed == ' ') ||
-                                      (*ssi->parsed == '\t') || (*ssi->parsed == '\n') ||
-                                      (*ssi->parsed == '\r'))) {
-          /* Move on to the next character in the buffer */
-          ssi->parse_left--;
-          ssi->parsed++;
-          break;
-        }
-
-        /* Have we found the next character we expect for the tag leadout? */
-        if (*ssi->parsed == http_ssi_tag_desc[ssi->tag_type].lead_out[ssi->tag_index]) {
-          /* Yes - move to the next one unless we have found the complete
-           * leadout, in which case we need to call the client to process
-           * the tag. */
-
-          /* Move on to the next character in the buffer */
-          ssi->parse_left--;
-          ssi->parsed++;
-          ssi->tag_index++;
-
-          if (http_ssi_tag_desc[ssi->tag_type].lead_out[ssi->tag_index] == 0) {
-            /* Call the client to ask for the insert string for the
-             * tag we just found. */
-#if LWIP_HTTPD_SSI_MULTIPART
-            ssi->tag_part = 0; /* start with tag part 0 */
-#endif /* LWIP_HTTPD_SSI_MULTIPART */
-            get_tag_insert(hs);
-
-            /* Next time through, we are going to be sending data
-             * immediately, either the end of the block we start
-             * sending here or the insert string. */
-            ssi->tag_index = 0;
-            ssi->tag_state = TAG_SENDING;
-            ssi->tag_end = ssi->parsed;
-#if !LWIP_HTTPD_SSI_INCLUDE_TAG
-            ssi->parsed = ssi->tag_started;
-#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/
-
-            /* If there is any unsent data in the buffer prior to the
-             * tag, we need to send it now. */
-            if (ssi->tag_end > hs->file) {
-              /* How much of the data can we send? */
-#if LWIP_HTTPD_SSI_INCLUDE_TAG
-              len = (u16_t)LWIP_MIN(ssi->tag_end - hs->file, 0xffff);
-#else /* LWIP_HTTPD_SSI_INCLUDE_TAG*/
-              /* we would include the tag in sending */
-              len = (u16_t)LWIP_MIN(ssi->tag_started - hs->file, 0xffff);
-#endif /* LWIP_HTTPD_SSI_INCLUDE_TAG*/
-
-              err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs));
-              if (err == ERR_OK) {
-                data_to_send = 1;
-#if !LWIP_HTTPD_SSI_INCLUDE_TAG
-                if (ssi->tag_started <= hs->file) {
-                  /* pretend to have sent the tag, too */
-                  len += (u16_t)(ssi->tag_end - ssi->tag_started);
-                }
-#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/
-                hs->file += len;
-                hs->left -= len;
-              }
-            }
-          }
-        } else {
-          /* We found an unexpected character so this is not a tag. Move
-           * back to idle state. */
-          ssi->parse_left--;
-          ssi->parsed++;
-          ssi->tag_state = TAG_NONE;
-        }
-        break;
-
-      /*
-       * We have found a valid tag and are in the process of sending
-       * data as a result of that discovery. We send either remaining data
-       * from the file prior to the insert point or the insert string itself.
-       */
-      case TAG_SENDING:
-        /* Do we have any remaining file data to send from the buffer prior
-         * to the tag? */
-        if (ssi->tag_end > hs->file) {
-          /* How much of the data can we send? */
-#if LWIP_HTTPD_SSI_INCLUDE_TAG
-          len = (u16_t)LWIP_MIN(ssi->tag_end - hs->file, 0xffff);
-#else /* LWIP_HTTPD_SSI_INCLUDE_TAG*/
-          LWIP_ASSERT("hs->started >= hs->file", ssi->tag_started >= hs->file);
-          /* we would include the tag in sending */
-          len = (u16_t)LWIP_MIN(ssi->tag_started - hs->file, 0xffff);
-#endif /* LWIP_HTTPD_SSI_INCLUDE_TAG*/
-          if (len != 0) {
-            err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs));
-          } else {
-            err = ERR_OK;
-          }
-          if (err == ERR_OK) {
-            data_to_send = 1;
-#if !LWIP_HTTPD_SSI_INCLUDE_TAG
-            if (ssi->tag_started <= hs->file) {
-              /* pretend to have sent the tag, too */
-              len += (u16_t)(ssi->tag_end - ssi->tag_started);
-            }
-#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/
-            hs->file += len;
-            hs->left -= len;
-          }
-        } else {
-#if LWIP_HTTPD_SSI_MULTIPART
-          if (ssi->tag_index >= ssi->tag_insert_len) {
-            /* Did the last SSIHandler have more to send? */
-            if (ssi->tag_part != HTTPD_LAST_TAG_PART) {
-              /* If so, call it again */
-              ssi->tag_index = 0;
-              get_tag_insert(hs);
-            }
-          }
-#endif /* LWIP_HTTPD_SSI_MULTIPART */
-
-          /* Do we still have insert data left to send? */
-          if (ssi->tag_index < ssi->tag_insert_len) {
-            /* We are sending the insert string itself. How much of the
-             * insert can we send? */
-            len = (ssi->tag_insert_len - ssi->tag_index);
-
-            /* Note that we set the copy flag here since we only have a
-             * single tag insert buffer per connection. If we don't do
-             * this, insert corruption can occur if more than one insert
-             * is processed before we call tcp_output. */
-            err = http_write(pcb, &(ssi->tag_insert[ssi->tag_index]), &len,
-                             HTTP_IS_TAG_VOLATILE(hs));
-            if (err == ERR_OK) {
-              data_to_send = 1;
-              ssi->tag_index += len;
-              /* Don't return here: keep on sending data */
-            }
-          } else {
-#if LWIP_HTTPD_SSI_MULTIPART
-            if (ssi->tag_part == HTTPD_LAST_TAG_PART)
-#endif /* LWIP_HTTPD_SSI_MULTIPART */
-            {
-              /* We have sent all the insert data so go back to looking for
-               * a new tag. */
-              LWIP_DEBUGF(HTTPD_DEBUG, ("Everything sent.\n"));
-              ssi->tag_index = 0;
-              ssi->tag_state = TAG_NONE;
-#if !LWIP_HTTPD_SSI_INCLUDE_TAG
-              ssi->parsed = ssi->tag_end;
-#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/
-            }
-          }
-          break;
-        default:
-          break;
-        }
-    }
-  }
-
-  /* If we drop out of the end of the for loop, this implies we must have
-   * file data to send so send it now. In TAG_SENDING state, we've already
-   * handled this so skip the send if that's the case. */
-  if ((ssi->tag_state != TAG_SENDING) && (ssi->parsed > hs->file)) {
-#if LWIP_HTTPD_DYNAMIC_FILE_READ && !LWIP_HTTPD_SSI_INCLUDE_TAG
-    if ((ssi->tag_state != TAG_NONE) && (ssi->tag_started > ssi->tag_end)) {
-      /* If we found tag on the edge of the read buffer: just throw away the first part
-         (we have copied/saved everything required for parsing on later). */
-      len = (u16_t)(ssi->tag_started - hs->file);
-      hs->left -= (ssi->parsed - ssi->tag_started);
-      ssi->parsed = ssi->tag_started;
-      ssi->tag_started = hs->buf;
-    } else
-#endif /* LWIP_HTTPD_DYNAMIC_FILE_READ && !LWIP_HTTPD_SSI_INCLUDE_TAG */
-    {
-      len = (u16_t)LWIP_MIN(ssi->parsed - hs->file, 0xffff);
-    }
-
-    err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs));
-    if (err == ERR_OK) {
-      data_to_send = 1;
-      hs->file += len;
-      hs->left -= len;
-    }
-  }
-  return data_to_send;
-}
-#endif /* LWIP_HTTPD_SSI */
-
-/**
- * Try to send more data on this pcb.
- *
- * @param pcb the pcb to send data
- * @param hs connection state
- */
-static u8_t
-http_send(struct altcp_pcb *pcb, struct http_state *hs)
-{
-  u8_t data_to_send = HTTP_NO_DATA_TO_SEND;
-
-  LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_send: pcb=%p hs=%p left=%d\n", (void *)pcb,
-              (void *)hs, hs != NULL ? (int)hs->left : 0));
-
-#if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND
-  if (hs->unrecved_bytes != 0) {
-    return 0;
-  }
-#endif /* LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND */
-
-  /* If we were passed a NULL state structure pointer, ignore the call. */
-  if (hs == NULL) {
-    return 0;
-  }
-
-#if LWIP_HTTPD_FS_ASYNC_READ
-  /* Check if we are allowed to read from this file.
-     (e.g. SSI might want to delay sending until data is available) */
-  if (!fs_is_file_ready(hs->handle, http_continue, hs)) {
-    return 0;
-  }
-#endif /* LWIP_HTTPD_FS_ASYNC_READ */
-
-#if LWIP_HTTPD_DYNAMIC_HEADERS
-  /* Do we have any more header data to send for this file? */
-  if (hs->hdr_index < NUM_FILE_HDR_STRINGS) {
-    data_to_send = http_send_headers(pcb, hs);
-    if ((data_to_send == HTTP_DATA_TO_SEND_FREED) ||
-        ((data_to_send != HTTP_DATA_TO_SEND_CONTINUE) &&
-         (hs->hdr_index < NUM_FILE_HDR_STRINGS))) {
-      return data_to_send;
-    }
-  }
-#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */
-
-  /* Have we run out of file data to send? If so, we need to read the next
-   * block from the file. */
-  if (hs->left == 0) {
-    if (!http_check_eof(pcb, hs)) {
-      return 0;
-    }
-  }
-
-#if LWIP_HTTPD_SSI
-  if (hs->ssi) {
-    data_to_send = http_send_data_ssi(pcb, hs);
-  } else
-#endif /* LWIP_HTTPD_SSI */
-  {
-    data_to_send = http_send_data_nonssi(pcb, hs);
-  }
-
-  if ((hs->left == 0) && (fs_bytes_left(hs->handle) <= 0)) {
-    /* We reached the end of the file so this request is done.
-     * This adds the FIN flag right into the last data segment. */
-    LWIP_DEBUGF(HTTPD_DEBUG, ("End of file.\n"));
-    http_eof(pcb, hs);
-    return 0;
-  }
-  LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("send_data end.\n"));
-  return data_to_send;
-}
-
-#if LWIP_HTTPD_SUPPORT_EXTSTATUS
-/** Initialize a http connection with a file to send for an error message
- *
- * @param hs http connection state
- * @param error_nr HTTP error number
- * @return ERR_OK if file was found and hs has been initialized correctly
- *         another err_t otherwise
- */
-static err_t
-http_find_error_file(struct http_state *hs, u16_t error_nr)
-{
-  const char *uri, *uri1, *uri2, *uri3;
-
-  if (error_nr == 501) {
-    uri1 = "/501.html";
-    uri2 = "/501.htm";
-    uri3 = "/501.shtml";
-  } else {
-    /* 400 (bad request is the default) */
-    uri1 = "/400.html";
-    uri2 = "/400.htm";
-    uri3 = "/400.shtml";
-  }
-  if (fs_open(&hs->file_handle, uri1) == ERR_OK) {
-    uri = uri1;
-  } else if (fs_open(&hs->file_handle, uri2) == ERR_OK) {
-    uri = uri2;
-  } else if (fs_open(&hs->file_handle, uri3) == ERR_OK) {
-    uri = uri3;
-  } else {
-    LWIP_DEBUGF(HTTPD_DEBUG, ("Error page for error %"U16_F" not found\n",
-                              error_nr));
-    return ERR_ARG;
-  }
-  return http_init_file(hs, &hs->file_handle, 0, uri, 0, NULL);
-}
-#else /* LWIP_HTTPD_SUPPORT_EXTSTATUS */
-#define http_find_error_file(hs, error_nr) ERR_ARG
-#endif /* LWIP_HTTPD_SUPPORT_EXTSTATUS */
-
-/**
- * Get the file struct for a 404 error page.
- * Tries some file names and returns NULL if none found.
- *
- * @param uri pointer that receives the actual file name URI
- * @return file struct for the error page or NULL no matching file was found
- */
-static struct fs_file *
-http_get_404_file(struct http_state *hs, const char **uri)
-{
-  err_t err;
-
-  *uri = "/404.html";
-  err = fs_open(&hs->file_handle, *uri);
-  if (err != ERR_OK) {
-    /* 404.html doesn't exist. Try 404.htm instead. */
-    *uri = "/404.htm";
-    err = fs_open(&hs->file_handle, *uri);
-    if (err != ERR_OK) {
-      /* 404.htm doesn't exist either. Try 404.shtml instead. */
-      *uri = "/404.shtml";
-      err = fs_open(&hs->file_handle, *uri);
-      if (err != ERR_OK) {
-        /* 404.htm doesn't exist either. Indicate to the caller that it should
-         * send back a default 404 page.
-         */
-        *uri = NULL;
-        return NULL;
-      }
-    }
-  }
-
-  return &hs->file_handle;
-}
-
-#if LWIP_HTTPD_SUPPORT_POST
-static err_t
-http_handle_post_finished(struct http_state *hs)
-{
-#if LWIP_HTTPD_POST_MANUAL_WND
-  /* Prevent multiple calls to httpd_post_finished, since it might have already
-     been called before from httpd_post_data_recved(). */
-  if (hs->post_finished) {
-    return ERR_OK;
-  }
-  hs->post_finished = 1;
-#endif /* LWIP_HTTPD_POST_MANUAL_WND */
-  /* application error or POST finished */
-  /* NULL-terminate the buffer */
-  http_uri_buf[0] = 0;
-  httpd_post_finished(hs, http_uri_buf, LWIP_HTTPD_URI_BUF_LEN);
-  return http_find_file(hs, http_uri_buf, 0);
-}
-
-/** Pass received POST body data to the application and correctly handle
- * returning a response document or closing the connection.
- * ATTENTION: The application is responsible for the pbuf now, so don't free it!
- *
- * @param hs http connection state
- * @param p pbuf to pass to the application
- * @return ERR_OK if passed successfully, another err_t if the response file
- *         hasn't been found (after POST finished)
- */
-static err_t
-http_post_rxpbuf(struct http_state *hs, struct pbuf *p)
-{
-  err_t err;
-
-  if (p != NULL) {
-    /* adjust remaining Content-Length */
-    if (hs->post_content_len_left < p->tot_len) {
-      hs->post_content_len_left = 0;
-    } else {
-      hs->post_content_len_left -= p->tot_len;
-    }
-  }
-#if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND
-  /* prevent connection being closed if httpd_post_data_recved() is called nested */
-  hs->unrecved_bytes++;
-#endif
-  if (p != NULL) {
-    err = httpd_post_receive_data(hs, p);
-  } else {
-    err = ERR_OK;
-  }
-#if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND
-  hs->unrecved_bytes--;
-#endif
-  if (err != ERR_OK) {
-    /* Ignore remaining content in case of application error */
-    hs->post_content_len_left = 0;
-  }
-  if (hs->post_content_len_left == 0) {
-#if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND
-    if (hs->unrecved_bytes != 0) {
-      return ERR_OK;
-    }
-#endif /* LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND */
-    /* application error or POST finished */
-    return http_handle_post_finished(hs);
-  }
-
-  return ERR_OK;
-}
-
-/** Handle a post request. Called from http_parse_request when method 'POST'
- * is found.
- *
- * @param p The input pbuf (containing the POST header and body).
- * @param hs The http connection state.
- * @param data HTTP request (header and part of body) from input pbuf(s).
- * @param data_len Size of 'data'.
- * @param uri The HTTP URI parsed from input pbuf(s).
- * @param uri_end Pointer to the end of 'uri' (here, the rest of the HTTP
- *                header starts).
- * @return ERR_OK: POST correctly parsed and accepted by the application.
- *         ERR_INPROGRESS: POST not completely parsed (no error yet)
- *         another err_t: Error parsing POST or denied by the application
- */
-static err_t
-http_post_request(struct pbuf *inp, struct http_state *hs,
-                  char *data, u16_t data_len, char *uri, char *uri_end)
-{
-  err_t err;
-  /* search for end-of-header (first double-CRLF) */
-  char *crlfcrlf = lwip_strnstr(uri_end + 1, CRLF CRLF, data_len - (uri_end + 1 - data));
-
-  if (crlfcrlf != NULL) {
-    /* search for "Content-Length: " */
-#define HTTP_HDR_CONTENT_LEN                "Content-Length: "
-#define HTTP_HDR_CONTENT_LEN_LEN            16
-#define HTTP_HDR_CONTENT_LEN_DIGIT_MAX_LEN  10
-    char *scontent_len = lwip_strnstr(uri_end + 1, HTTP_HDR_CONTENT_LEN, crlfcrlf - (uri_end + 1));
-    if (scontent_len != NULL) {
-      char *scontent_len_end = lwip_strnstr(scontent_len + HTTP_HDR_CONTENT_LEN_LEN, CRLF, HTTP_HDR_CONTENT_LEN_DIGIT_MAX_LEN);
-      if (scontent_len_end != NULL) {
-        int content_len;
-        char *content_len_num = scontent_len + HTTP_HDR_CONTENT_LEN_LEN;
-        content_len = atoi(content_len_num);
-        if (content_len == 0) {
-          /* if atoi returns 0 on error, fix this */
-          if ((content_len_num[0] != '0') || (content_len_num[1] != '\r')) {
-            content_len = -1;
-          }
-        }
-        if (content_len >= 0) {
-          /* adjust length of HTTP header passed to application */
-          const char *hdr_start_after_uri = uri_end + 1;
-          u16_t hdr_len = (u16_t)LWIP_MIN(data_len, crlfcrlf + 4 - data);
-          u16_t hdr_data_len = (u16_t)LWIP_MIN(data_len, crlfcrlf + 4 - hdr_start_after_uri);
-          u8_t post_auto_wnd = 1;
-          http_uri_buf[0] = 0;
-          /* trim http header */
-          *crlfcrlf = 0;
-          err = httpd_post_begin(hs, uri, hdr_start_after_uri, hdr_data_len, content_len,
-                                 http_uri_buf, LWIP_HTTPD_URI_BUF_LEN, &post_auto_wnd);
-          if (err == ERR_OK) {
-            /* try to pass in data of the first pbuf(s) */
-            struct pbuf *q = inp;
-            u16_t start_offset = hdr_len;
-#if LWIP_HTTPD_POST_MANUAL_WND
-            hs->no_auto_wnd = !post_auto_wnd;
-#endif /* LWIP_HTTPD_POST_MANUAL_WND */
-            /* set the Content-Length to be received for this POST */
-            hs->post_content_len_left = (u32_t)content_len;
-
-            /* get to the pbuf where the body starts */
-            while ((q != NULL) && (q->len <= start_offset)) {
-              start_offset -= q->len;
-              q = q->next;
-            }
-            if (q != NULL) {
-              /* hide the remaining HTTP header */
-              pbuf_remove_header(q, start_offset);
-#if LWIP_HTTPD_POST_MANUAL_WND
-              if (!post_auto_wnd) {
-                /* already tcp_recved() this data... */
-                hs->unrecved_bytes = q->tot_len;
-              }
-#endif /* LWIP_HTTPD_POST_MANUAL_WND */
-              pbuf_ref(q);
-              return http_post_rxpbuf(hs, q);
-            } else if (hs->post_content_len_left == 0) {
-              q = pbuf_alloc(PBUF_RAW, 0, PBUF_REF);
-              return http_post_rxpbuf(hs, q);
-            } else {
-              return ERR_OK;
-            }
-          } else {
-            /* return file passed from application */
-            return http_find_file(hs, http_uri_buf, 0);
-          }
-        } else {
-          LWIP_DEBUGF(HTTPD_DEBUG, ("POST received invalid Content-Length: %s\n",
-                                    content_len_num));
-          return ERR_ARG;
-        }
-      }
-    }
-    /* If we come here, headers are fully received (double-crlf), but Content-Length
-       was not included. Since this is currently the only supported method, we have
-       to fail in this case! */
-    LWIP_DEBUGF(HTTPD_DEBUG, ("Error when parsing Content-Length\n"));
-    return ERR_ARG;
-  }
-  /* if we come here, the POST is incomplete */
-#if LWIP_HTTPD_SUPPORT_REQUESTLIST
-  return ERR_INPROGRESS;
-#else /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
-  return ERR_ARG;
-#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
-}
-
-#if LWIP_HTTPD_POST_MANUAL_WND
-/**
- * @ingroup httpd
- * A POST implementation can call this function to update the TCP window.
- * This can be used to throttle data reception (e.g. when received data is
- * programmed to flash and data is received faster than programmed).
- *
- * @param connection A connection handle passed to httpd_post_begin for which
- *        httpd_post_finished has *NOT* been called yet!
- * @param recved_len Length of data received (for window update)
- */
-void httpd_post_data_recved(void *connection, u16_t recved_len)
-{
-  struct http_state *hs = (struct http_state *)connection;
-  if (hs != NULL) {
-    if (hs->no_auto_wnd) {
-      u16_t len = recved_len;
-      if (hs->unrecved_bytes >= recved_len) {
-        hs->unrecved_bytes -= recved_len;
-      } else {
-        LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_LEVEL_WARNING, ("httpd_post_data_recved: recved_len too big\n"));
-        len = (u16_t)hs->unrecved_bytes;
-        hs->unrecved_bytes = 0;
-      }
-      if (hs->pcb != NULL) {
-        if (len != 0) {
-          altcp_recved(hs->pcb, len);
-        }
-        if ((hs->post_content_len_left == 0) && (hs->unrecved_bytes == 0)) {
-          /* finished handling POST */
-          http_handle_post_finished(hs);
-          http_send(hs->pcb, hs);
-        }
-      }
-    }
-  }
-}
-#endif /* LWIP_HTTPD_POST_MANUAL_WND */
-
-#endif /* LWIP_HTTPD_SUPPORT_POST */
-
-#if LWIP_HTTPD_FS_ASYNC_READ
-/** Try to send more data if file has been blocked before
- * This is a callback function passed to fs_read_async().
- */
-static void
-http_continue(void *connection)
-{
-  struct http_state *hs = (struct http_state *)connection;
-  LWIP_ASSERT_CORE_LOCKED();
-  if (hs && (hs->pcb) && (hs->handle)) {
-    LWIP_ASSERT("hs->pcb != NULL", hs->pcb != NULL);
-    LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("httpd_continue: try to send more data\n"));
-    if (http_send(hs->pcb, hs)) {
-      /* If we wrote anything to be sent, go ahead and send it now. */
-      LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("tcp_output\n"));
-      altcp_output(hs->pcb);
-    }
-  }
-}
-#endif /* LWIP_HTTPD_FS_ASYNC_READ */
-
-/**
- * When data has been received in the correct state, try to parse it
- * as a HTTP request.
- *
- * @param inp the received pbuf
- * @param hs the connection state
- * @param pcb the altcp_pcb which received this packet
- * @return ERR_OK if request was OK and hs has been initialized correctly
- *         ERR_INPROGRESS if request was OK so far but not fully received
- *         another err_t otherwise
- */
-static err_t
-http_parse_request(struct pbuf *inp, struct http_state *hs, struct altcp_pcb *pcb)
-{
-  char *data;
-  char *crlf;
-  u16_t data_len;
-  struct pbuf *p = inp;
-#if LWIP_HTTPD_SUPPORT_REQUESTLIST
-  u16_t clen;
-#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
-#if LWIP_HTTPD_SUPPORT_POST
-  err_t err;
-#endif /* LWIP_HTTPD_SUPPORT_POST */
-
-  LWIP_UNUSED_ARG(pcb); /* only used for post */
-  LWIP_ASSERT("p != NULL", p != NULL);
-  LWIP_ASSERT("hs != NULL", hs != NULL);
-
-  if ((hs->handle != NULL) || (hs->file != NULL)) {
-    LWIP_DEBUGF(HTTPD_DEBUG, ("Received data while sending a file\n"));
-    /* already sending a file */
-    /* @todo: abort? */
-    return ERR_USE;
-  }
-
-#if LWIP_HTTPD_SUPPORT_REQUESTLIST
-
-  LWIP_DEBUGF(HTTPD_DEBUG, ("Received %"U16_F" bytes\n", p->tot_len));
-
-  /* first check allowed characters in this pbuf? */
-
-  /* enqueue the pbuf */
-  if (hs->req == NULL) {
-    LWIP_DEBUGF(HTTPD_DEBUG, ("First pbuf\n"));
-    hs->req = p;
-  } else {
-    LWIP_DEBUGF(HTTPD_DEBUG, ("pbuf enqueued\n"));
-    pbuf_cat(hs->req, p);
-  }
-  /* increase pbuf ref counter as it is freed when we return but we want to
-     keep it on the req list */
-  pbuf_ref(p);
-
-  if (hs->req->next != NULL) {
-    data_len = LWIP_MIN(hs->req->tot_len, LWIP_HTTPD_MAX_REQ_LENGTH);
-    pbuf_copy_partial(hs->req, httpd_req_buf, data_len, 0);
-    data = httpd_req_buf;
-  } else
-#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
-  {
-    data = (char *)p->payload;
-    data_len = p->len;
-    if (p->len != p->tot_len) {
-      LWIP_DEBUGF(HTTPD_DEBUG, ("Warning: incomplete header due to chained pbufs\n"));
-    }
-  }
-
-  /* received enough data for minimal request? */
-  if (data_len >= MIN_REQ_LEN) {
-    /* wait for CRLF before parsing anything */
-    crlf = lwip_strnstr(data, CRLF, data_len);
-    if (crlf != NULL) {
-#if LWIP_HTTPD_SUPPORT_POST
-      int is_post = 0;
-#endif /* LWIP_HTTPD_SUPPORT_POST */
-      int is_09 = 0;
-      char *sp1, *sp2;
-      u16_t left_len, uri_len;
-      LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("CRLF received, parsing request\n"));
-      /* parse method */
-      if (!strncmp(data, "GET ", 4)) {
-        sp1 = data + 3;
-        /* received GET request */
-        LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Received GET request\"\n"));
-#if LWIP_HTTPD_SUPPORT_POST
-      } else if (!strncmp(data, "POST ", 5)) {
-        /* store request type */
-        is_post = 1;
-        sp1 = data + 4;
-        /* received GET request */
-        LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Received POST request\n"));
-#endif /* LWIP_HTTPD_SUPPORT_POST */
-      } else {
-        /* null-terminate the METHOD (pbuf is freed anyway wen returning) */
-        data[4] = 0;
-        /* unsupported method! */
-        LWIP_DEBUGF(HTTPD_DEBUG, ("Unsupported request method (not implemented): \"%s\"\n",
-                                  data));
-        return http_find_error_file(hs, 501);
-      }
-      /* if we come here, method is OK, parse URI */
-      left_len = (u16_t)(data_len - ((sp1 + 1) - data));
-      sp2 = lwip_strnstr(sp1 + 1, " ", left_len);
-#if LWIP_HTTPD_SUPPORT_V09
-      if (sp2 == NULL) {
-        /* HTTP 0.9: respond with correct protocol version */
-        sp2 = lwip_strnstr(sp1 + 1, CRLF, left_len);
-        is_09 = 1;
-#if LWIP_HTTPD_SUPPORT_POST
-        if (is_post) {
-          /* HTTP/0.9 does not support POST */
-          goto badrequest;
-        }
-#endif /* LWIP_HTTPD_SUPPORT_POST */
-      }
-#endif /* LWIP_HTTPD_SUPPORT_V09 */
-      uri_len = (u16_t)(sp2 - (sp1 + 1));
-      if ((sp2 != 0) && (sp2 > sp1)) {
-        /* wait for CRLFCRLF (indicating end of HTTP headers) before parsing anything */
-        if (lwip_strnstr(data, CRLF CRLF, data_len) != NULL) {
-          char *uri = sp1 + 1;
-#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
-          /* This is HTTP/1.0 compatible: for strict 1.1, a connection
-             would always be persistent unless "close" was specified. */
-          if (!is_09 && (lwip_strnstr(data, HTTP11_CONNECTIONKEEPALIVE, data_len) ||
-                         lwip_strnstr(data, HTTP11_CONNECTIONKEEPALIVE2, data_len))) {
-            hs->keepalive = 1;
-          } else {
-            hs->keepalive = 0;
-          }
-#endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */
-          /* null-terminate the METHOD (pbuf is freed anyway wen returning) */
-          *sp1 = 0;
-          uri[uri_len] = 0;
-          LWIP_DEBUGF(HTTPD_DEBUG, ("Received \"%s\" request for URI: \"%s\"\n",
-                                    data, uri));
-#if LWIP_HTTPD_SUPPORT_POST
-          if (is_post) {
-#if LWIP_HTTPD_SUPPORT_REQUESTLIST
-            struct pbuf *q = hs->req;
-#else /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
-            struct pbuf *q = inp;
-#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
-            err = http_post_request(q, hs, data, data_len, uri, sp2);
-            if (err != ERR_OK) {
-              /* restore header for next try */
-              *sp1 = ' ';
-              *sp2 = ' ';
-              uri[uri_len] = ' ';
-            }
-            if (err == ERR_ARG) {
-              goto badrequest;
-            }
-            return err;
-          } else
-#endif /* LWIP_HTTPD_SUPPORT_POST */
-          {
-            return http_find_file(hs, uri, is_09);
-          }
-        }
-      } else {
-        LWIP_DEBUGF(HTTPD_DEBUG, ("invalid URI\n"));
-      }
-    }
-  }
-
-#if LWIP_HTTPD_SUPPORT_REQUESTLIST
-  clen = pbuf_clen(hs->req);
-  if ((hs->req->tot_len <= LWIP_HTTPD_REQ_BUFSIZE) &&
-      (clen <= LWIP_HTTPD_REQ_QUEUELEN)) {
-    /* request not fully received (too short or CRLF is missing) */
-    return ERR_INPROGRESS;
-  } else
-#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
-  {
-#if LWIP_HTTPD_SUPPORT_POST
-badrequest:
-#endif /* LWIP_HTTPD_SUPPORT_POST */
-    LWIP_DEBUGF(HTTPD_DEBUG, ("bad request\n"));
-    /* could not parse request */
-    return http_find_error_file(hs, 400);
-  }
-}
-
-#if LWIP_HTTPD_SSI && (LWIP_HTTPD_SSI_BY_FILE_EXTENSION == 1)
-/* Check if SSI should be parsed for this file/URL
- * (With LWIP_HTTPD_SSI_BY_FILE_EXTENSION == 2, this function can be
- * overridden by an external implementation.)
- *
- * @return 1 for SSI, 0 for standard files
- */
-static u8_t
-http_uri_is_ssi(struct fs_file *file, const char *uri)
-{
-  size_t loop;
-  u8_t tag_check = 0;
-  if (file != NULL) {
-    /* See if we have been asked for an shtml file and, if so,
-        enable tag checking. */
-    const char *ext = NULL, *sub;
-    char *param = (char *)strstr(uri, "?");
-    if (param != NULL) {
-      /* separate uri from parameters for now, set back later */
-      *param = 0;
-    }
-    sub = uri;
-    ext = uri;
-    for (sub = strstr(sub, "."); sub != NULL; sub = strstr(sub, ".")) {
-      ext = sub;
-      sub++;
-    }
-    for (loop = 0; loop < NUM_SHTML_EXTENSIONS; loop++) {
-      if (!lwip_stricmp(ext, g_pcSSIExtensions[loop])) {
-        tag_check = 1;
-        break;
-      }
-    }
-    if (param != NULL) {
-      *param = '?';
-    }
-  }
-  return tag_check;
-}
-#endif /* LWIP_HTTPD_SSI */
-
-/** Try to find the file specified by uri and, if found, initialize hs
- * accordingly.
- *
- * @param hs the connection state
- * @param uri the HTTP header URI
- * @param is_09 1 if the request is HTTP/0.9 (no HTTP headers in response)
- * @return ERR_OK if file was found and hs has been initialized correctly
- *         another err_t otherwise
- */
-static err_t
-http_find_file(struct http_state *hs, const char *uri, int is_09)
-{
-  size_t loop;
-  struct fs_file *file = NULL;
-  char *params = NULL;
-  err_t err;
-#if LWIP_HTTPD_CGI
-  int i;
-#endif /* LWIP_HTTPD_CGI */
-#if !LWIP_HTTPD_SSI
-  const
-#endif /* !LWIP_HTTPD_SSI */
-  /* By default, assume we will not be processing server-side-includes tags */
-  u8_t tag_check = 0;
-
-  /* Have we been asked for the default file (in root or a directory) ? */
-#if LWIP_HTTPD_MAX_REQUEST_URI_LEN
-  size_t uri_len = strlen(uri);
-  if ((uri_len > 0) && (uri[uri_len - 1] == '/') &&
-      ((uri != http_uri_buf) || (uri_len == 1))) {
-    size_t copy_len = LWIP_MIN(sizeof(http_uri_buf) - 1, uri_len - 1);
-    if (copy_len > 0) {
-      MEMCPY(http_uri_buf, uri, copy_len);
-      http_uri_buf[copy_len] = 0;
-    }
-#else /* LWIP_HTTPD_MAX_REQUEST_URI_LEN */
-  if ((uri[0] == '/') &&  (uri[1] == 0)) {
-#endif /* LWIP_HTTPD_MAX_REQUEST_URI_LEN */
-    /* Try each of the configured default filenames until we find one
-       that exists. */
-    for (loop = 0; loop < NUM_DEFAULT_FILENAMES; loop++) {
-      const char *file_name;
-#if LWIP_HTTPD_MAX_REQUEST_URI_LEN
-      if (copy_len > 0) {
-        size_t len_left = sizeof(http_uri_buf) - copy_len - 1;
-        if (len_left > 0) {
-          size_t name_len = strlen(httpd_default_filenames[loop].name);
-          size_t name_copy_len = LWIP_MIN(len_left, name_len);
-          MEMCPY(&http_uri_buf[copy_len], httpd_default_filenames[loop].name, name_copy_len);
-          http_uri_buf[copy_len + name_copy_len] = 0;
-        }
-        file_name = http_uri_buf;
-      } else
-#endif /* LWIP_HTTPD_MAX_REQUEST_URI_LEN */
-      {
-        file_name = httpd_default_filenames[loop].name;
-      }
-      LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Looking for %s...\n", file_name));
-      err = fs_open(&hs->file_handle, file_name);
-      if (err == ERR_OK) {
-        uri = file_name;
-        file = &hs->file_handle;
-        LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Opened.\n"));
-#if LWIP_HTTPD_SSI
-        tag_check = httpd_default_filenames[loop].shtml;
-#endif /* LWIP_HTTPD_SSI */
-        break;
-      }
-    }
-  }
-  if (file == NULL) {
-    /* No - we've been asked for a specific file. */
-    /* First, isolate the base URI (without any parameters) */
-    params = (char *)strchr(uri, '?');
-    if (params != NULL) {
-      /* URI contains parameters. NULL-terminate the base URI */
-      *params = '\0';
-      params++;
-    }
-
-#if LWIP_HTTPD_CGI
-    http_cgi_paramcount = -1;
-    /* Does the base URI we have isolated correspond to a CGI handler? */
-    if (httpd_num_cgis && httpd_cgis) {
-      for (i = 0; i < httpd_num_cgis; i++) {
-        if (strcmp(uri, httpd_cgis[i].pcCGIName) == 0) {
-          /*
-           * We found a CGI that handles this URI so extract the
-           * parameters and call the handler.
-           */
-          http_cgi_paramcount = extract_uri_parameters(hs, params);
-          uri = httpd_cgis[i].pfnCGIHandler(i, http_cgi_paramcount, hs->params,
-                                         hs->param_vals);
-          break;
-        }
-      }
-    }
-#endif /* LWIP_HTTPD_CGI */
-
-    LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Opening %s\n", uri));
-
-    err = fs_open(&hs->file_handle, uri);
-    if (err == ERR_OK) {
-      file = &hs->file_handle;
-    } else {
-      file = http_get_404_file(hs, &uri);
-    }
-#if LWIP_HTTPD_SSI
-    if (file != NULL) {
-      if (file->flags & FS_FILE_FLAGS_SSI) {
-        tag_check = 1;
-      } else {
-#if LWIP_HTTPD_SSI_BY_FILE_EXTENSION
-        tag_check = http_uri_is_ssi(file, uri);
-#endif /* LWIP_HTTPD_SSI_BY_FILE_EXTENSION */
-      }
-    }
-#endif /* LWIP_HTTPD_SSI */
-  }
-  if (file == NULL) {
-    /* None of the default filenames exist so send back a 404 page */
-    file = http_get_404_file(hs, &uri);
-  }
-  return http_init_file(hs, file, is_09, uri, tag_check, params);
-}
-
-/** Initialize a http connection with a file to send (if found).
- * Called by http_find_file and http_find_error_file.
- *
- * @param hs http connection state
- * @param file file structure to send (or NULL if not found)
- * @param is_09 1 if the request is HTTP/0.9 (no HTTP headers in response)
- * @param uri the HTTP header URI
- * @param tag_check enable SSI tag checking
- * @param params != NULL if URI has parameters (separated by '?')
- * @return ERR_OK if file was found and hs has been initialized correctly
- *         another err_t otherwise
- */
-static err_t
-http_init_file(struct http_state *hs, struct fs_file *file, int is_09, const char *uri,
-               u8_t tag_check, char *params)
-{
-#if !LWIP_HTTPD_SUPPORT_V09
-  LWIP_UNUSED_ARG(is_09);
-#endif
-  if (file != NULL) {
-    /* file opened, initialise struct http_state */
-#if !LWIP_HTTPD_DYNAMIC_FILE_READ
-    /* If dynamic read is disabled, file data must be in one piece and available now */
-    LWIP_ASSERT("file->data != NULL", file->data != NULL);
-#endif
-
-#if LWIP_HTTPD_SSI
-    if (tag_check) {
-      struct http_ssi_state *ssi = http_ssi_state_alloc();
-      if (ssi != NULL) {
-        ssi->tag_index = 0;
-        ssi->tag_state = TAG_NONE;
-        ssi->parsed = file->data;
-        ssi->parse_left = file->len;
-        ssi->tag_end = file->data;
-        hs->ssi = ssi;
-      }
-    }
-#else /* LWIP_HTTPD_SSI */
-    LWIP_UNUSED_ARG(tag_check);
-#endif /* LWIP_HTTPD_SSI */
-    hs->handle = file;
-#if LWIP_HTTPD_CGI_SSI
-    if (params != NULL) {
-      /* URI contains parameters, call generic CGI handler */
-      int count;
-#if LWIP_HTTPD_CGI
-      if (http_cgi_paramcount >= 0) {
-        count = http_cgi_paramcount;
-      } else
-#endif
-      {
-        count = extract_uri_parameters(hs, params);
-      }
-      httpd_cgi_handler(file, uri, count, http_cgi_params, http_cgi_param_vals
-#if defined(LWIP_HTTPD_FILE_STATE) && LWIP_HTTPD_FILE_STATE
-                        , file->state
-#endif /* LWIP_HTTPD_FILE_STATE */
-                       );
-    }
-#else /* LWIP_HTTPD_CGI_SSI */
-    LWIP_UNUSED_ARG(params);
-#endif /* LWIP_HTTPD_CGI_SSI */
-    hs->file = file->data;
-    LWIP_ASSERT("File length must be positive!", (file->len >= 0));
-#if LWIP_HTTPD_CUSTOM_FILES
-    if (file->is_custom_file && (file->data == NULL)) {
-      /* custom file, need to read data first (via fs_read_custom) */
-      hs->left = 0;
-    } else
-#endif /* LWIP_HTTPD_CUSTOM_FILES */
-    {
-      hs->left = (u32_t)file->len;
-    }
-    hs->retries = 0;
-#if LWIP_HTTPD_TIMING
-    hs->time_started = sys_now();
-#endif /* LWIP_HTTPD_TIMING */
-#if !LWIP_HTTPD_DYNAMIC_HEADERS
-    LWIP_ASSERT("HTTP headers not included in file system",
-                (hs->handle->flags & FS_FILE_FLAGS_HEADER_INCLUDED) != 0);
-#endif /* !LWIP_HTTPD_DYNAMIC_HEADERS */
-#if LWIP_HTTPD_SUPPORT_V09
-    if (is_09 && ((hs->handle->flags & FS_FILE_FLAGS_HEADER_INCLUDED) != 0)) {
-      /* HTTP/0.9 responses are sent without HTTP header,
-         search for the end of the header. */
-      char *file_start = lwip_strnstr(hs->file, CRLF CRLF, hs->left);
-      if (file_start != NULL) {
-        int diff = file_start + 4 - hs->file;
-        hs->file += diff;
-        hs->left -= (u32_t)diff;
-      }
-    }
-#endif /* LWIP_HTTPD_SUPPORT_V09*/
-  } else {
-    hs->handle = NULL;
-    hs->file = NULL;
-    hs->left = 0;
-    hs->retries = 0;
-  }
-#if LWIP_HTTPD_DYNAMIC_HEADERS
-  /* Determine the HTTP headers to send based on the file extension of
-   * the requested URI. */
-  if ((hs->handle == NULL) || ((hs->handle->flags & FS_FILE_FLAGS_HEADER_INCLUDED) == 0)) {
-    get_http_headers(hs, uri);
-  }
-#else /* LWIP_HTTPD_DYNAMIC_HEADERS */
-  LWIP_UNUSED_ARG(uri);
-#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */
-#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
-  if (hs->keepalive) {
-#if LWIP_HTTPD_SSI
-    if (hs->ssi != NULL) {
-      hs->keepalive = 0;
-    } else
-#endif /* LWIP_HTTPD_SSI */
-    {
-      if ((hs->handle != NULL) &&
-          ((hs->handle->flags & (FS_FILE_FLAGS_HEADER_INCLUDED | FS_FILE_FLAGS_HEADER_PERSISTENT)) == FS_FILE_FLAGS_HEADER_INCLUDED)) {
-        hs->keepalive = 0;
-      }
-    }
-  }
-#endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */
-  return ERR_OK;
-}
-
-/**
- * The pcb had an error and is already deallocated.
- * The argument might still be valid (if != NULL).
- */
-static void
-http_err(void *arg, err_t err)
-{
-  struct http_state *hs = (struct http_state *)arg;
-  LWIP_UNUSED_ARG(err);
-
-  LWIP_DEBUGF(HTTPD_DEBUG, ("http_err: %s", lwip_strerr(err)));
-
-  if (hs != NULL) {
-    http_state_free(hs);
-  }
-}
-
-/**
- * Data has been sent and acknowledged by the remote host.
- * This means that more data can be sent.
- */
-static err_t
-http_sent(void *arg, struct altcp_pcb *pcb, u16_t len)
-{
-  struct http_state *hs = (struct http_state *)arg;
-
-  LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_sent %p\n", (void *)pcb));
-
-  LWIP_UNUSED_ARG(len);
-
-  if (hs == NULL) {
-    return ERR_OK;
-  }
-
-  hs->retries = 0;
-
-  http_send(pcb, hs);
-
-  return ERR_OK;
-}
-
-/**
- * The poll function is called every 2nd second.
- * If there has been no data sent (which resets the retries) in 8 seconds, close.
- * If the last portion of a file has not been sent in 2 seconds, close.
- *
- * This could be increased, but we don't want to waste resources for bad connections.
- */
-static err_t
-http_poll(void *arg, struct altcp_pcb *pcb)
-{
-  struct http_state *hs = (struct http_state *)arg;
-  LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_poll: pcb=%p hs=%p pcb_state=%s\n",
-              (void *)pcb, (void *)hs, tcp_debug_state_str(altcp_dbg_get_tcp_state(pcb))));
-
-  if (hs == NULL) {
-    err_t closed;
-    /* arg is null, close. */
-    LWIP_DEBUGF(HTTPD_DEBUG, ("http_poll: arg is NULL, close\n"));
-    closed = http_close_conn(pcb, NULL);
-    LWIP_UNUSED_ARG(closed);
-#if LWIP_HTTPD_ABORT_ON_CLOSE_MEM_ERROR
-    if (closed == ERR_MEM) {
-      altcp_abort(pcb);
-      return ERR_ABRT;
-    }
-#endif /* LWIP_HTTPD_ABORT_ON_CLOSE_MEM_ERROR */
-    return ERR_OK;
-  } else {
-    hs->retries++;
-    if (hs->retries == HTTPD_MAX_RETRIES) {
-      LWIP_DEBUGF(HTTPD_DEBUG, ("http_poll: too many retries, close\n"));
-      http_close_conn(pcb, hs);
-      return ERR_OK;
-    }
-
-    /* If this connection has a file open, try to send some more data. If
-     * it has not yet received a GET request, don't do this since it will
-     * cause the connection to close immediately. */
-    if (hs->handle) {
-      LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_poll: try to send more data\n"));
-      if (http_send(pcb, hs)) {
-        /* If we wrote anything to be sent, go ahead and send it now. */
-        LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("tcp_output\n"));
-        altcp_output(pcb);
-      }
-    }
-  }
-
-  return ERR_OK;
-}
-
-/**
- * Data has been received on this pcb.
- * For HTTP 1.0, this should normally only happen once (if the request fits in one packet).
- */
-static err_t
-http_recv(void *arg, struct altcp_pcb *pcb, struct pbuf *p, err_t err)
-{
-  struct http_state *hs = (struct http_state *)arg;
-  LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_recv: pcb=%p pbuf=%p err=%s\n", (void *)pcb,
-              (void *)p, lwip_strerr(err)));
-
-  if ((err != ERR_OK) || (p == NULL) || (hs == NULL)) {
-    /* error or closed by other side? */
-    if (p != NULL) {
-      /* Inform TCP that we have taken the data. */
-      altcp_recved(pcb, p->tot_len);
-      pbuf_free(p);
-    }
-    if (hs == NULL) {
-      /* this should not happen, only to be robust */
-      LWIP_DEBUGF(HTTPD_DEBUG, ("Error, http_recv: hs is NULL, close\n"));
-    }
-    http_close_conn(pcb, hs);
-    return ERR_OK;
-  }
-
-#if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND
-  if (hs->no_auto_wnd) {
-    hs->unrecved_bytes += p->tot_len;
-  } else
-#endif /* LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND */
-  {
-    /* Inform TCP that we have taken the data. */
-    altcp_recved(pcb, p->tot_len);
-  }
-
-#if LWIP_HTTPD_SUPPORT_POST
-  if (hs->post_content_len_left > 0) {
-    /* reset idle counter when POST data is received */
-    hs->retries = 0;
-    /* this is data for a POST, pass the complete pbuf to the application */
-    http_post_rxpbuf(hs, p);
-    /* pbuf is passed to the application, don't free it! */
-    if (hs->post_content_len_left == 0) {
-      /* all data received, send response or close connection */
-      http_send(pcb, hs);
-    }
-    return ERR_OK;
-  } else
-#endif /* LWIP_HTTPD_SUPPORT_POST */
-  {
-    if (hs->handle == NULL) {
-      err_t parsed = http_parse_request(p, hs, pcb);
-      LWIP_ASSERT("http_parse_request: unexpected return value", parsed == ERR_OK
-                  || parsed == ERR_INPROGRESS || parsed == ERR_ARG || parsed == ERR_USE);
-#if LWIP_HTTPD_SUPPORT_REQUESTLIST
-      if (parsed != ERR_INPROGRESS) {
-        /* request fully parsed or error */
-        if (hs->req != NULL) {
-          pbuf_free(hs->req);
-          hs->req = NULL;
-        }
-      }
-#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
-      pbuf_free(p);
-      if (parsed == ERR_OK) {
-#if LWIP_HTTPD_SUPPORT_POST
-        if (hs->post_content_len_left == 0)
-#endif /* LWIP_HTTPD_SUPPORT_POST */
-        {
-          LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_recv: data %p len %"S32_F"\n", (const void *)hs->file, hs->left));
-          http_send(pcb, hs);
-        }
-      } else if (parsed == ERR_ARG) {
-        /* @todo: close on ERR_USE? */
-        http_close_conn(pcb, hs);
-      }
-    } else {
-      LWIP_DEBUGF(HTTPD_DEBUG, ("http_recv: already sending data\n"));
-      /* already sending but still receiving data, we might want to RST here? */
-      pbuf_free(p);
-    }
-  }
-  return ERR_OK;
-}
-
-/**
- * A new incoming connection has been accepted.
- */
-static err_t
-http_accept(void *arg, struct altcp_pcb *pcb, err_t err)
-{
-  struct http_state *hs;
-  LWIP_UNUSED_ARG(err);
-  LWIP_UNUSED_ARG(arg);
-  LWIP_DEBUGF(HTTPD_DEBUG, ("http_accept %p / %p\n", (void *)pcb, arg));
-
-  if ((err != ERR_OK) || (pcb == NULL)) {
-    return ERR_VAL;
-  }
-
-  /* Set priority */
-  altcp_setprio(pcb, HTTPD_TCP_PRIO);
-
-  /* Allocate memory for the structure that holds the state of the
-     connection - initialized by that function. */
-  hs = http_state_alloc();
-  if (hs == NULL) {
-    LWIP_DEBUGF(HTTPD_DEBUG, ("http_accept: Out of memory, RST\n"));
-    return ERR_MEM;
-  }
-  hs->pcb = pcb;
-
-  /* Tell TCP that this is the structure we wish to be passed for our
-     callbacks. */
-  altcp_arg(pcb, hs);
-
-  /* Set up the various callback functions */
-  altcp_recv(pcb, http_recv);
-  altcp_err(pcb, http_err);
-  altcp_poll(pcb, http_poll, HTTPD_POLL_INTERVAL);
-  altcp_sent(pcb, http_sent);
-
-  return ERR_OK;
-}
-
-static void
-httpd_init_pcb(struct altcp_pcb *pcb, u16_t port)
-{
-  err_t err;
-
-  if (pcb) {
-    altcp_setprio(pcb, HTTPD_TCP_PRIO);
-    /* set SOF_REUSEADDR here to explicitly bind httpd to multiple interfaces */
-    err = altcp_bind(pcb, IP_ANY_TYPE, port);
-    LWIP_UNUSED_ARG(err); /* in case of LWIP_NOASSERT */
-    LWIP_ASSERT("httpd_init: tcp_bind failed", err == ERR_OK);
-    pcb = altcp_listen(pcb);
-    LWIP_ASSERT("httpd_init: tcp_listen failed", pcb != NULL);
-    altcp_accept(pcb, http_accept);
-  }
-}
-
-/**
- * @ingroup httpd
- * Initialize the httpd: set up a listening PCB and bind it to the defined port
- */
-void
-httpd_init(void)
-{
-  struct altcp_pcb *pcb;
-
-#if HTTPD_USE_MEM_POOL
-  LWIP_MEMPOOL_INIT(HTTPD_STATE);
-#if LWIP_HTTPD_SSI
-  LWIP_MEMPOOL_INIT(HTTPD_SSI_STATE);
-#endif
-#endif
-  LWIP_DEBUGF(HTTPD_DEBUG, ("httpd_init\n"));
-
-  /* LWIP_ASSERT_CORE_LOCKED(); is checked by tcp_new() */
-
-  pcb = altcp_tcp_new_ip_type(IPADDR_TYPE_ANY);
-  LWIP_ASSERT("httpd_init: tcp_new failed", pcb != NULL);
-  httpd_init_pcb(pcb, HTTPD_SERVER_PORT);
-}
-
-#if HTTPD_ENABLE_HTTPS
-/**
- * @ingroup httpd
- * Initialize the httpd: set up a listening PCB and bind it to the defined port.
- * Also set up TLS connection handling (HTTPS).
- */
-void
-httpd_inits(struct altcp_tls_config *conf)
-{
-#if LWIP_ALTCP_TLS
-  struct altcp_pcb *pcb_tls = altcp_tls_new(conf, IPADDR_TYPE_ANY);
-  LWIP_ASSERT("httpd_init: altcp_tls_new failed", pcb_tls != NULL);
-  httpd_init_pcb(pcb_tls, HTTPD_SERVER_PORT_HTTPS);
-#else /* LWIP_ALTCP_TLS */
-  LWIP_UNUSED_ARG(conf);
-#endif /* LWIP_ALTCP_TLS */
-}
-#endif /* HTTPD_ENABLE_HTTPS */
-
-#if LWIP_HTTPD_SSI
-/**
- * @ingroup httpd
- * Set the SSI handler function.
- *
- * @param ssi_handler the SSI handler function
- * @param tags an array of SSI tag strings to search for in SSI-enabled files
- * @param num_tags number of tags in the 'tags' array
- */
-void
-http_set_ssi_handler(tSSIHandler ssi_handler, const char **tags, int num_tags)
-{
-  LWIP_DEBUGF(HTTPD_DEBUG, ("http_set_ssi_handler\n"));
-
-  LWIP_ASSERT("no ssi_handler given", ssi_handler != NULL);
-  httpd_ssi_handler = ssi_handler;
-
-#if LWIP_HTTPD_SSI_RAW
-  LWIP_UNUSED_ARG(tags);
-  LWIP_UNUSED_ARG(num_tags);
-#else /* LWIP_HTTPD_SSI_RAW */
-  LWIP_ASSERT("no tags given", tags != NULL);
-  LWIP_ASSERT("invalid number of tags", num_tags > 0);
-
-  httpd_tags = tags;
-  httpd_num_tags = num_tags;
-#endif /* !LWIP_HTTPD_SSI_RAW */
-}
-#endif /* LWIP_HTTPD_SSI */
-
-#if LWIP_HTTPD_CGI
-/**
- * @ingroup httpd
- * Set an array of CGI filenames/handler functions
- *
- * @param cgis an array of CGI filenames/handler functions
- * @param num_handlers number of elements in the 'cgis' array
- */
-void
-http_set_cgi_handlers(const tCGI *cgis, int num_handlers)
-{
-  LWIP_ASSERT("no cgis given", cgis != NULL);
-  LWIP_ASSERT("invalid number of handlers", num_handlers > 0);
-
-  httpd_cgis = cgis;
-  httpd_num_cgis = num_handlers;
-}
-#endif /* LWIP_HTTPD_CGI */
-
-#endif /* LWIP_TCP && LWIP_CALLBACK_API */
+/**
+ * @file
+ * LWIP HTTP server implementation
+ */
+
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *         Simon Goldschmidt
+ *
+ */
+
+/**
+ * @defgroup httpd HTTP server
+ * @ingroup apps
+ *
+ * This httpd supports for a
+ * rudimentary server-side-include facility which will replace tags of the form
+ * <!--#tag--> in any file whose extension is .shtml, .shtm or .ssi with
+ * strings provided by an include handler whose pointer is provided to the
+ * module via function http_set_ssi_handler().
+ * Additionally, a simple common
+ * gateway interface (CGI) handling mechanism has been added to allow clients
+ * to hook functions to particular request URIs.
+ *
+ * To enable SSI support, define label LWIP_HTTPD_SSI in lwipopts.h.
+ * To enable CGI support, define label LWIP_HTTPD_CGI in lwipopts.h.
+ *
+ * By default, the server assumes that HTTP headers are already present in
+ * each file stored in the file system.  By defining LWIP_HTTPD_DYNAMIC_HEADERS in
+ * lwipopts.h, this behavior can be changed such that the server inserts the
+ * headers automatically based on the extension of the file being served.  If
+ * this mode is used, be careful to ensure that the file system image used
+ * does not already contain the header information.
+ *
+ * File system images without headers can be created using the makefsfile
+ * tool with the -h command line option.
+ *
+ *
+ * Notes about valid SSI tags
+ * --------------------------
+ *
+ * The following assumptions are made about tags used in SSI markers:
+ *
+ * 1. No tag may contain '-' or whitespace characters within the tag name.
+ * 2. Whitespace is allowed between the tag leadin "<!--#" and the start of
+ *    the tag name and between the tag name and the leadout string "-->".
+ * 3. The maximum tag name length is LWIP_HTTPD_MAX_TAG_NAME_LEN, currently 8 characters.
+ *
+ * Notes on CGI usage
+ * ------------------
+ *
+ * The simple CGI support offered here works with GET method requests only
+ * and can handle up to 16 parameters encoded into the URI. The handler
+ * function may not write directly to the HTTP output but must return a
+ * filename that the HTTP server will send to the browser as a response to
+ * the incoming CGI request.
+ *
+ *
+ *
+ * The list of supported file types is quite short, so if makefsdata complains
+ * about an unknown extension, make sure to add it (and its doctype) to
+ * the 'g_psHTTPHeaders' list.
+ */
+#include "lwip/init.h"
+#include "lwip/apps/httpd.h"
+#include "lwip/debug.h"
+#include "lwip/stats.h"
+#include "lwip/apps/fs.h"
+#include "httpd_structs.h"
+#include "lwip/def.h"
+#include "lwip/ip.h"
+#include "lwip/tcp.h"
+
+#include <string.h> /* memset */
+#include <stdlib.h> /* atoi */
+#ifdef PRINTF_STDLIB
+#include <stdio.h>
+#endif
+#ifdef PRINTF_CUSTOM
+#include "tinystdio.h"
+#endif
+
+#if LWIP_TCP && LWIP_CALLBACK_API
+
+/** Minimum length for a valid HTTP/0.9 request: "GET /\r\n" -> 7 bytes */
+#define MIN_REQ_LEN   7
+
+#define CRLF "\r\n"
+#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
+#define HTTP11_CONNECTIONKEEPALIVE  "Connection: keep-alive"
+#define HTTP11_CONNECTIONKEEPALIVE2 "Connection: Keep-Alive"
+#endif
+
+/** These defines check whether tcp_write has to copy data or not */
+
+/** This was TI's check whether to let TCP copy data or not
+ * \#define HTTP_IS_DATA_VOLATILE(hs) ((hs->file < (char *)0x20000000) ? 0 : TCP_WRITE_FLAG_COPY)
+ */
+#ifndef HTTP_IS_DATA_VOLATILE
+#if LWIP_HTTPD_SSI
+/* Copy for SSI files, no copy for non-SSI files */
+#define HTTP_IS_DATA_VOLATILE(hs)   ((hs)->ssi ? TCP_WRITE_FLAG_COPY : 0)
+#else /* LWIP_HTTPD_SSI */
+/** Default: don't copy if the data is sent from file-system directly */
+#define HTTP_IS_DATA_VOLATILE(hs) (((hs->file != NULL) && (hs->handle != NULL) && (hs->file == \
+                                   (const char*)hs->handle->data + hs->handle->len - hs->left)) \
+                                   ? 0 : TCP_WRITE_FLAG_COPY)
+#endif /* LWIP_HTTPD_SSI */
+#endif
+
+/** Default: headers are sent from ROM */
+#ifndef HTTP_IS_HDR_VOLATILE
+#define HTTP_IS_HDR_VOLATILE(hs, ptr) 0
+#endif
+
+/* Return values for http_send_*() */
+#define HTTP_DATA_TO_SEND_BREAK    2
+#define HTTP_DATA_TO_SEND_CONTINUE 1
+#define HTTP_NO_DATA_TO_SEND       0
+
+typedef struct
+{
+  const char *name;
+  u8_t shtml;
+} default_filename;
+
+const default_filename g_psDefaultFilenames[] = {
+  {"/index.shtml", 1 },
+  {"/index.ssi",   1 },
+  {"/index.shtm",  1 },
+  {"/index.html",  0 },
+  {"/index.htm",   0 }
+};
+
+#define NUM_DEFAULT_FILENAMES (sizeof(g_psDefaultFilenames) /   \
+                               sizeof(default_filename))
+
+#if LWIP_HTTPD_SUPPORT_REQUESTLIST
+/** HTTP request is copied here from pbufs for simple parsing */
+static char httpd_req_buf[LWIP_HTTPD_MAX_REQ_LENGTH+1];
+#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
+
+#if LWIP_HTTPD_SUPPORT_POST
+#if LWIP_HTTPD_POST_MAX_RESPONSE_URI_LEN > LWIP_HTTPD_MAX_REQUEST_URI_LEN
+#define LWIP_HTTPD_URI_BUF_LEN LWIP_HTTPD_POST_MAX_RESPONSE_URI_LEN
+#endif
+#endif
+#ifndef LWIP_HTTPD_URI_BUF_LEN
+#define LWIP_HTTPD_URI_BUF_LEN LWIP_HTTPD_MAX_REQUEST_URI_LEN
+#endif
+#if LWIP_HTTPD_URI_BUF_LEN
+/* Filename for response file to send when POST is finished or
+ * search for default files when a directory is requested. */
+static char http_uri_buf[LWIP_HTTPD_URI_BUF_LEN+1];
+#endif
+
+#if LWIP_HTTPD_DYNAMIC_HEADERS
+/* The number of individual strings that comprise the headers sent before each
+ * requested file.
+ */
+#define NUM_FILE_HDR_STRINGS 5
+#define HDR_STRINGS_IDX_HTTP_STATUS          0 /* e.g. "HTTP/1.0 200 OK\r\n" */
+#define HDR_STRINGS_IDX_SERVER_NAME          1 /* e.g. "Server: "HTTPD_SERVER_AGENT"\r\n" */
+#define HDR_STRINGS_IDX_CONTENT_LEN_KEPALIVE 2 /* e.g. "Content-Length: xy\r\n" and/or "Connection: keep-alive\r\n" */
+#define HDR_STRINGS_IDX_CONTENT_LEN_NR       3 /* the byte count, when content-length is used */
+#define HDR_STRINGS_IDX_CONTENT_TYPE         4 /* the content type (or default answer content type including default document) */
+
+/* The dynamically generated Content-Length buffer needs space for CRLF + NULL */
+#define LWIP_HTTPD_MAX_CONTENT_LEN_OFFSET 3
+#ifndef LWIP_HTTPD_MAX_CONTENT_LEN_SIZE
+/* The dynamically generated Content-Length buffer shall be able to work with
+   ~953 MB (9 digits) */
+#define LWIP_HTTPD_MAX_CONTENT_LEN_SIZE   (9 + LWIP_HTTPD_MAX_CONTENT_LEN_OFFSET)
+#endif
+#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */
+
+#if LWIP_HTTPD_SSI
+
+#define HTTPD_LAST_TAG_PART 0xFFFF
+
+enum tag_check_state {
+  TAG_NONE,       /* Not processing an SSI tag */
+  TAG_LEADIN,     /* Tag lead in "<!--#" being processed */
+  TAG_FOUND,      /* Tag name being read, looking for lead-out start */
+  TAG_LEADOUT,    /* Tag lead out "-->" being processed */
+  TAG_SENDING     /* Sending tag replacement string */
+};
+
+struct http_ssi_state {
+  const char *parsed;     /* Pointer to the first unparsed byte in buf. */
+#if !LWIP_HTTPD_SSI_INCLUDE_TAG
+  const char *tag_started;/* Pointer to the first opening '<' of the tag. */
+#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG */
+  const char *tag_end;    /* Pointer to char after the closing '>' of the tag. */
+  u32_t parse_left; /* Number of unparsed bytes in buf. */
+  u16_t tag_index;   /* Counter used by tag parsing state machine */
+  u16_t tag_insert_len; /* Length of insert in string tag_insert */
+#if LWIP_HTTPD_SSI_MULTIPART
+  u16_t tag_part; /* Counter passed to and changed by tag insertion function to insert multiple times */
+#endif /* LWIP_HTTPD_SSI_MULTIPART */
+  u8_t tag_name_len; /* Length of the tag name in string tag_name */
+  char tag_name[LWIP_HTTPD_MAX_TAG_NAME_LEN + 1]; /* Last tag name extracted */
+  char tag_insert[LWIP_HTTPD_MAX_TAG_INSERT_LEN + 1]; /* Insert string for tag_name */
+  enum tag_check_state tag_state; /* State of the tag processor */
+};
+#endif /* LWIP_HTTPD_SSI */
+
+struct http_state {
+#if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED
+  struct http_state *next;
+#endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */
+  struct fs_file file_handle;
+  struct fs_file *handle;
+  const char *file;       /* Pointer to first unsent byte in buf. */
+
+  struct tcp_pcb *pcb;
+#if LWIP_HTTPD_SUPPORT_REQUESTLIST
+  struct pbuf *req;
+#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
+
+#if LWIP_HTTPD_DYNAMIC_FILE_READ
+  char *buf;        /* File read buffer. */
+  int buf_len;      /* Size of file read buffer, buf. */
+#endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */
+  u32_t left;       /* Number of unsent bytes in buf. */
+  u8_t retries;
+#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
+  u8_t keepalive;
+#endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */
+#if LWIP_HTTPD_SSI
+  struct http_ssi_state *ssi;
+#endif /* LWIP_HTTPD_SSI */
+#if LWIP_HTTPD_CGI
+  char *params[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Params extracted from the request URI */
+  char *param_vals[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Values for each extracted param */
+#endif /* LWIP_HTTPD_CGI */
+#if LWIP_HTTPD_DYNAMIC_HEADERS
+  const char *hdrs[NUM_FILE_HDR_STRINGS]; /* HTTP headers to be sent. */
+  char hdr_content_len[LWIP_HTTPD_MAX_CONTENT_LEN_SIZE];
+  u16_t hdr_pos;     /* The position of the first unsent header byte in the
+                        current string */
+  u16_t hdr_index;   /* The index of the hdr string currently being sent. */
+#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */
+#if LWIP_HTTPD_TIMING
+  u32_t time_started;
+#endif /* LWIP_HTTPD_TIMING */
+#if LWIP_HTTPD_SUPPORT_POST
+  u32_t post_content_len_left;
+#if LWIP_HTTPD_POST_MANUAL_WND
+  u32_t unrecved_bytes;
+  u8_t no_auto_wnd;
+  u8_t post_finished;
+#endif /* LWIP_HTTPD_POST_MANUAL_WND */
+#endif /* LWIP_HTTPD_SUPPORT_POST*/
+};
+
+#if HTTPD_USE_MEM_POOL
+LWIP_MEMPOOL_DECLARE(HTTPD_STATE,     MEMP_NUM_PARALLEL_HTTPD_CONNS,     sizeof(struct http_state),     "HTTPD_STATE")
+#if LWIP_HTTPD_SSI
+LWIP_MEMPOOL_DECLARE(HTTPD_SSI_STATE, MEMP_NUM_PARALLEL_HTTPD_SSI_CONNS, sizeof(struct http_ssi_state), "HTTPD_SSI_STATE")
+#define HTTP_FREE_SSI_STATE(x)  LWIP_MEMPOOL_FREE(HTTPD_SSI_STATE, (x))
+#define HTTP_ALLOC_SSI_STATE()  (struct http_ssi_state *)LWIP_MEMPOOL_ALLOC(HTTPD_SSI_STATE)
+#endif /* LWIP_HTTPD_SSI */
+#define HTTP_ALLOC_HTTP_STATE() (struct http_state *)LWIP_MEMPOOL_ALLOC(HTTPD_STATE)
+#define HTTP_FREE_HTTP_STATE(x) LWIP_MEMPOOL_FREE(HTTPD_STATE, (x))
+#else /* HTTPD_USE_MEM_POOL */
+#define HTTP_ALLOC_HTTP_STATE() (struct http_state *)mem_malloc(sizeof(struct http_state))
+#define HTTP_FREE_HTTP_STATE(x) mem_free(x)
+#if LWIP_HTTPD_SSI
+#define HTTP_ALLOC_SSI_STATE()  (struct http_ssi_state *)mem_malloc(sizeof(struct http_ssi_state))
+#define HTTP_FREE_SSI_STATE(x)  mem_free(x)
+#endif /* LWIP_HTTPD_SSI */
+#endif /* HTTPD_USE_MEM_POOL */
+
+static err_t http_close_conn(struct tcp_pcb *pcb, struct http_state *hs);
+static err_t http_close_or_abort_conn(struct tcp_pcb *pcb, struct http_state *hs, u8_t abort_conn);
+static err_t http_find_file(struct http_state *hs, const char *uri, int is_09);
+static err_t http_init_file(struct http_state *hs, struct fs_file *file, int is_09, const char *uri, u8_t tag_check, char* params);
+static err_t http_poll(void *arg, struct tcp_pcb *pcb);
+static u8_t http_check_eof(struct tcp_pcb *pcb, struct http_state *hs);
+#if LWIP_HTTPD_FS_ASYNC_READ
+static void http_continue(void *connection);
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+
+#if LWIP_HTTPD_SSI
+/* SSI insert handler function pointer. */
+tSSIHandler g_pfnSSIHandler;
+#if !LWIP_HTTPD_SSI_RAW
+int g_iNumTags;
+const char **g_ppcTags;
+#endif /* !LWIP_HTTPD_SSI_RAW */
+
+#define LEN_TAG_LEAD_IN 5
+const char * const g_pcTagLeadIn = "<!--#";
+
+#define LEN_TAG_LEAD_OUT 3
+const char * const g_pcTagLeadOut = "-->";
+#endif /* LWIP_HTTPD_SSI */
+
+#if LWIP_HTTPD_CGI
+/* CGI handler information */
+const tCGI *g_pCGIs;
+int g_iNumCGIs;
+int http_cgi_paramcount;
+#define http_cgi_params     hs->params
+#define http_cgi_param_vals hs->param_vals
+#elif LWIP_HTTPD_CGI_SSI
+char *http_cgi_params[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Params extracted from the request URI */
+char *http_cgi_param_vals[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Values for each extracted param */
+#endif /* LWIP_HTTPD_CGI */
+
+#if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED
+/** global list of active HTTP connections, use to kill the oldest when
+    running out of memory */
+static struct http_state *http_connections;
+
+static void
+http_add_connection(struct http_state *hs)
+{
+  /* add the connection to the list */
+  hs->next = http_connections;
+  http_connections = hs;
+}
+
+static void
+http_remove_connection(struct http_state *hs)
+{
+  /* take the connection off the list */
+  if (http_connections) {
+    if (http_connections == hs) {
+      http_connections = hs->next;
+    } else {
+      struct http_state *last;
+      for(last = http_connections; last->next != NULL; last = last->next) {
+        if (last->next == hs) {
+          last->next = hs->next;
+          break;
+        }
+      }
+    }
+  }
+}
+
+static void
+http_kill_oldest_connection(u8_t ssi_required)
+{
+  struct http_state *hs = http_connections;
+  struct http_state *hs_free_next = NULL;
+  while(hs && hs->next) {
+#if LWIP_HTTPD_SSI
+    if (ssi_required) {
+      if (hs->next->ssi != NULL) {
+        hs_free_next = hs;
+      }
+    } else
+#else /* LWIP_HTTPD_SSI */
+    LWIP_UNUSED_ARG(ssi_required);
+#endif /* LWIP_HTTPD_SSI */
+    {
+      hs_free_next = hs;
+    }
+    LWIP_ASSERT("broken list", hs != hs->next);
+    hs = hs->next;
+  }
+  if (hs_free_next != NULL) {
+    LWIP_ASSERT("hs_free_next->next != NULL", hs_free_next->next != NULL);
+    LWIP_ASSERT("hs_free_next->next->pcb != NULL", hs_free_next->next->pcb != NULL);
+    /* send RST when killing a connection because of memory shortage */
+    http_close_or_abort_conn(hs_free_next->next->pcb, hs_free_next->next, 1); /* this also unlinks the http_state from the list */
+  }
+}
+#else /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */
+
+#define http_add_connection(hs)
+#define http_remove_connection(hs)
+
+#endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */
+
+#if LWIP_HTTPD_SSI
+/** Allocate as struct http_ssi_state. */
+static struct http_ssi_state*
+http_ssi_state_alloc(void)
+{
+  struct http_ssi_state *ret = HTTP_ALLOC_SSI_STATE();
+#if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED
+  if (ret == NULL) {
+    http_kill_oldest_connection(1);
+    ret = HTTP_ALLOC_SSI_STATE();
+  }
+#endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */
+  if (ret != NULL) {
+    memset(ret, 0, sizeof(struct http_ssi_state));
+  }
+  return ret;
+}
+
+/** Free a struct http_ssi_state. */
+static void
+http_ssi_state_free(struct http_ssi_state *ssi)
+{
+  if (ssi != NULL) {
+    HTTP_FREE_SSI_STATE(ssi);
+  }
+}
+#endif /* LWIP_HTTPD_SSI */
+
+/** Initialize a struct http_state.
+ */
+static void
+http_state_init(struct http_state* hs)
+{
+  /* Initialize the structure. */
+  memset(hs, 0, sizeof(struct http_state));
+#if LWIP_HTTPD_DYNAMIC_HEADERS
+  /* Indicate that the headers are not yet valid */
+  hs->hdr_index = NUM_FILE_HDR_STRINGS;
+#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */
+}
+
+/** Allocate a struct http_state. */
+static struct http_state*
+http_state_alloc(void)
+{
+  struct http_state *ret = HTTP_ALLOC_HTTP_STATE();
+#if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED
+  if (ret == NULL) {
+    http_kill_oldest_connection(0);
+    ret = HTTP_ALLOC_HTTP_STATE();
+  }
+#endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */
+  if (ret != NULL) {
+    http_state_init(ret);
+    http_add_connection(ret);
+  }
+  return ret;
+}
+
+/** Free a struct http_state.
+ * Also frees the file data if dynamic.
+ */
+static void
+http_state_eof(struct http_state *hs)
+{
+  if(hs->handle) {
+#if LWIP_HTTPD_TIMING
+    u32_t ms_needed = sys_now() - hs->time_started;
+    u32_t needed = LWIP_MAX(1, (ms_needed/100));
+    LWIP_DEBUGF(HTTPD_DEBUG_TIMING, ("httpd: needed %"U32_F" ms to send file of %d bytes -> %"U32_F" bytes/sec\n",
+      ms_needed, hs->handle->len, ((((u32_t)hs->handle->len) * 10) / needed)));
+#endif /* LWIP_HTTPD_TIMING */
+    fs_close(hs->handle);
+    hs->handle = NULL;
+  }
+#if LWIP_HTTPD_DYNAMIC_FILE_READ
+  if (hs->buf != NULL) {
+    mem_free(hs->buf);
+    hs->buf = NULL;
+  }
+#endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */
+#if LWIP_HTTPD_SSI
+  if (hs->ssi) {
+    http_ssi_state_free(hs->ssi);
+    hs->ssi = NULL;
+  }
+#endif /* LWIP_HTTPD_SSI */
+#if LWIP_HTTPD_SUPPORT_REQUESTLIST
+  if (hs->req) {
+    pbuf_free(hs->req);
+    hs->req = NULL;
+  }
+#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
+}
+
+/** Free a struct http_state.
+ * Also frees the file data if dynamic.
+ */
+static void
+http_state_free(struct http_state *hs)
+{
+  if (hs != NULL) {
+    http_state_eof(hs);
+    http_remove_connection(hs);
+    HTTP_FREE_HTTP_STATE(hs);
+  }
+}
+
+/** Call tcp_write() in a loop trying smaller and smaller length
+ *
+ * @param pcb tcp_pcb to send
+ * @param ptr Data to send
+ * @param length Length of data to send (in/out: on return, contains the
+ *        amount of data sent)
+ * @param apiflags directly passed to tcp_write
+ * @return the return value of tcp_write
+ */
+static err_t
+http_write(struct tcp_pcb *pcb, const void* ptr, u16_t *length, u8_t apiflags)
+{
+  u16_t len, max_len;
+  err_t err;
+  LWIP_ASSERT("length != NULL", length != NULL);
+  len = *length;
+  if (len == 0) {
+    return ERR_OK;
+  }
+  /* We cannot send more data than space available in the send buffer. */
+  max_len = tcp_sndbuf(pcb);
+  if (max_len < len) {
+    len = max_len;
+  }
+#ifdef HTTPD_MAX_WRITE_LEN
+  /* Additional limitation: e.g. don't enqueue more than 2*mss at once */
+  max_len = HTTPD_MAX_WRITE_LEN(pcb);
+  if(len > max_len) {
+    len = max_len;
+  }
+#endif /* HTTPD_MAX_WRITE_LEN */
+  do {
+    LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Trying go send %d bytes\n", len));
+    err = tcp_write(pcb, ptr, len, apiflags);
+    if (err == ERR_MEM) {
+      if ((tcp_sndbuf(pcb) == 0) ||
+        (tcp_sndqueuelen(pcb) >= TCP_SND_QUEUELEN)) {
+          /* no need to try smaller sizes */
+          len = 1;
+      } else {
+        len /= 2;
+      }
+      LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, 
+        ("Send failed, trying less (%d bytes)\n", len));
+    }
+  } while ((err == ERR_MEM) && (len > 1));
+
+  if (err == ERR_OK) {
+    LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Sent %d bytes\n", len));
+    *length = len;
+  } else {
+    LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Send failed with err %d (\"%s\")\n", err, lwip_strerr(err)));
+    *length = 0;
+  }
+
+#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
+   /* ensure nagle is normally enabled (only disabled for persistent connections
+      when all data has been enqueued but the connection stays open for the next
+      request */
+   tcp_nagle_enable(pcb);
+#endif
+
+  return err;
+}
+
+/**
+ * The connection shall be actively closed (using RST to close from fault states).
+ * Reset the sent- and recv-callbacks.
+ *
+ * @param pcb the tcp pcb to reset callbacks
+ * @param hs connection state to free
+ */
+static err_t
+http_close_or_abort_conn(struct tcp_pcb *pcb, struct http_state *hs, u8_t abort_conn)
+{
+  err_t err;
+  LWIP_DEBUGF(HTTPD_DEBUG, ("Closing connection %p\n", (void*)pcb));
+
+#if LWIP_HTTPD_SUPPORT_POST
+  if (hs != NULL) {
+    if ((hs->post_content_len_left != 0)
+#if LWIP_HTTPD_POST_MANUAL_WND
+       || ((hs->no_auto_wnd != 0) && (hs->unrecved_bytes != 0))
+#endif /* LWIP_HTTPD_POST_MANUAL_WND */
+       ) {
+      /* make sure the post code knows that the connection is closed */
+      http_uri_buf[0] = 0;
+      httpd_post_finished(hs, http_uri_buf, LWIP_HTTPD_URI_BUF_LEN);
+    }
+  }
+#endif /* LWIP_HTTPD_SUPPORT_POST*/
+
+
+  tcp_arg(pcb, NULL);
+  tcp_recv(pcb, NULL);
+  tcp_err(pcb, NULL);
+  tcp_poll(pcb, NULL, 0);
+  tcp_sent(pcb, NULL);
+  if (hs != NULL) {
+    http_state_free(hs);
+  }
+
+  if (abort_conn) {
+    tcp_abort(pcb);
+    return ERR_OK;
+  }
+  err = tcp_close(pcb);
+  if (err != ERR_OK) {
+    LWIP_DEBUGF(HTTPD_DEBUG, ("Error %d closing %p\n", err, (void*)pcb));
+    /* error closing, try again later in poll */
+    tcp_poll(pcb, http_poll, HTTPD_POLL_INTERVAL);
+  }
+  return err;
+}
+
+/**
+ * The connection shall be actively closed.
+ * Reset the sent- and recv-callbacks.
+ *
+ * @param pcb the tcp pcb to reset callbacks
+ * @param hs connection state to free
+ */
+static err_t
+http_close_conn(struct tcp_pcb *pcb, struct http_state *hs)
+{
+   return http_close_or_abort_conn(pcb, hs, 0);
+}
+
+/** End of file: either close the connection (Connection: close) or
+ * close the file (Connection: keep-alive)
+ */
+static void
+http_eof(struct tcp_pcb *pcb, struct http_state *hs)
+{
+  /* HTTP/1.1 persistent connection? (Not supported for SSI) */
+#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
+  if (hs->keepalive) {
+    http_remove_connection(hs);
+
+    http_state_eof(hs);
+    http_state_init(hs);
+    /* restore state: */
+    hs->pcb = pcb;
+    hs->keepalive = 1;
+    http_add_connection(hs);
+    /* ensure nagle doesn't interfere with sending all data as fast as possible: */
+    tcp_nagle_disable(pcb);
+  } else
+#endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */
+  {
+    http_close_conn(pcb, hs);
+  }
+}
+
+#if LWIP_HTTPD_CGI || LWIP_HTTPD_CGI_SSI
+/**
+ * Extract URI parameters from the parameter-part of an URI in the form
+ * "test.cgi?x=y" @todo: better explanation!
+ * Pointers to the parameters are stored in hs->param_vals.
+ *
+ * @param hs http connection state
+ * @param params pointer to the NULL-terminated parameter string from the URI
+ * @return number of parameters extracted
+ */
+static int
+extract_uri_parameters(struct http_state *hs, char *params)
+{
+  char *pair;
+  char *equals;
+  int loop;
+
+  LWIP_UNUSED_ARG(hs);
+
+  /* If we have no parameters at all, return immediately. */
+  if(!params || (params[0] == '\0')) {
+      return(0);
+  }
+
+  /* Get a pointer to our first parameter */
+  pair = params;
+
+  /* Parse up to LWIP_HTTPD_MAX_CGI_PARAMETERS from the passed string and ignore the
+   * remainder (if any) */
+  for(loop = 0; (loop < LWIP_HTTPD_MAX_CGI_PARAMETERS) && pair; loop++) {
+
+    /* Save the name of the parameter */
+    http_cgi_params[loop] = pair;
+
+    /* Remember the start of this name=value pair */
+    equals = pair;
+
+    /* Find the start of the next name=value pair and replace the delimiter
+     * with a 0 to terminate the previous pair string. */
+    pair = strchr(pair, '&');
+    if(pair) {
+      *pair = '\0';
+      pair++;
+    } else {
+       /* We didn't find a new parameter so find the end of the URI and
+        * replace the space with a '\0' */
+        pair = strchr(equals, ' ');
+        if(pair) {
+            *pair = '\0';
+        }
+
+        /* Revert to NULL so that we exit the loop as expected. */
+        pair = NULL;
+    }
+
+    /* Now find the '=' in the previous pair, replace it with '\0' and save
+     * the parameter value string. */
+    equals = strchr(equals, '=');
+    if(equals) {
+      *equals = '\0';
+      http_cgi_param_vals[loop] = equals + 1;
+    } else {
+      http_cgi_param_vals[loop] = NULL;
+    }
+  }
+
+  return loop;
+}
+#endif /* LWIP_HTTPD_CGI || LWIP_HTTPD_CGI_SSI */
+
+#if LWIP_HTTPD_SSI
+/**
+ * Insert a tag (found in an shtml in the form of "<!--#tagname-->" into the file.
+ * The tag's name is stored in ssi->tag_name (NULL-terminated), the replacement
+ * should be written to hs->tag_insert (up to a length of LWIP_HTTPD_MAX_TAG_INSERT_LEN).
+ * The amount of data written is stored to ssi->tag_insert_len.
+ *
+ * @todo: return tag_insert_len - maybe it can be removed from struct http_state?
+ *
+ * @param hs http connection state
+ */
+static void
+get_tag_insert(struct http_state *hs)
+{
+#if LWIP_HTTPD_SSI_RAW
+  const char* tag;
+#else /* LWIP_HTTPD_SSI_RAW */
+  int tag;
+#endif /* LWIP_HTTPD_SSI_RAW */
+  size_t len;
+  struct http_ssi_state *ssi;
+#if LWIP_HTTPD_SSI_MULTIPART
+  u16_t current_tag_part;
+#endif /* LWIP_HTTPD_SSI_MULTIPART */
+
+  LWIP_ASSERT("hs != NULL", hs != NULL);
+  ssi = hs->ssi;
+  LWIP_ASSERT("ssi != NULL", ssi != NULL);
+#if LWIP_HTTPD_SSI_MULTIPART
+  current_tag_part = ssi->tag_part;
+  ssi->tag_part = HTTPD_LAST_TAG_PART;
+#endif /* LWIP_HTTPD_SSI_MULTIPART */
+#if LWIP_HTTPD_SSI_RAW
+  tag = ssi->tag_name;
+#endif
+
+  if(g_pfnSSIHandler
+#if !LWIP_HTTPD_SSI_RAW
+     && g_ppcTags && g_iNumTags
+#endif /* !LWIP_HTTPD_SSI_RAW */
+     ) {
+
+    /* Find this tag in the list we have been provided. */
+#if LWIP_HTTPD_SSI_RAW
+    {
+#else /* LWIP_HTTPD_SSI_RAW */
+    for(tag = 0; tag < g_iNumTags; tag++) {
+      if(strcmp(ssi->tag_name, g_ppcTags[tag]) == 0)
+#endif /* LWIP_HTTPD_SSI_RAW */
+      {
+        ssi->tag_insert_len = g_pfnSSIHandler(tag, ssi->tag_insert,
+           LWIP_HTTPD_MAX_TAG_INSERT_LEN
+#if LWIP_HTTPD_SSI_MULTIPART
+           , current_tag_part, &ssi->tag_part
+#endif /* LWIP_HTTPD_SSI_MULTIPART */
+#if LWIP_HTTPD_FILE_STATE
+           , (hs->handle ? hs->handle->state : NULL)
+#endif /* LWIP_HTTPD_FILE_STATE */
+           );
+#if LWIP_HTTPD_SSI_RAW
+        if (ssi->tag_insert_len != HTTPD_SSI_TAG_UNKNOWN)
+#endif /* LWIP_HTTPD_SSI_RAW */
+        {
+          return;
+        }
+      }
+    }
+  }
+
+  /* If we drop out, we were asked to serve a page which contains tags that
+   * we don't have a handler for. Merely echo back the tags with an error
+   * marker. */
+#define UNKNOWN_TAG1_TEXT "<b>***UNKNOWN TAG "
+#define UNKNOWN_TAG1_LEN  18
+#define UNKNOWN_TAG2_TEXT "***</b>"
+#define UNKNOWN_TAG2_LEN  7
+  len = LWIP_MIN(sizeof(ssi->tag_name), LWIP_MIN(strlen(ssi->tag_name),
+    LWIP_HTTPD_MAX_TAG_INSERT_LEN - (UNKNOWN_TAG1_LEN + UNKNOWN_TAG2_LEN)));
+  MEMCPY(ssi->tag_insert, UNKNOWN_TAG1_TEXT, UNKNOWN_TAG1_LEN);
+  MEMCPY(&ssi->tag_insert[UNKNOWN_TAG1_LEN], ssi->tag_name, len);
+  MEMCPY(&ssi->tag_insert[UNKNOWN_TAG1_LEN + len], UNKNOWN_TAG2_TEXT, UNKNOWN_TAG2_LEN);
+  ssi->tag_insert[UNKNOWN_TAG1_LEN + len + UNKNOWN_TAG2_LEN] = 0;
+
+  len = strlen(ssi->tag_insert);
+  LWIP_ASSERT("len <= 0xffff", len <= 0xffff);
+  ssi->tag_insert_len = (u16_t)len;
+}
+#endif /* LWIP_HTTPD_SSI */
+
+#if LWIP_HTTPD_DYNAMIC_HEADERS
+/**
+ * Generate the relevant HTTP headers for the given filename and write
+ * them into the supplied buffer.
+ */
+static void
+get_http_headers(struct http_state *hs, const char *uri)
+{
+  size_t content_type;
+  char *tmp;
+  char *ext;
+  char *vars;
+  u8_t add_content_len;
+
+  /* In all cases, the second header we send is the server identification
+     so set it here. */
+  hs->hdrs[HDR_STRINGS_IDX_SERVER_NAME] = g_psHTTPHeaderStrings[HTTP_HDR_SERVER];
+  hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEPALIVE] = NULL;
+  hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_NR] = NULL;
+
+  /* Is this a normal file or the special case we use to send back the
+     default "404: Page not found" response? */
+  if (uri == NULL) {
+    hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_NOT_FOUND];
+#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
+    if (hs->keepalive) {
+      hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = g_psHTTPHeaderStrings[DEFAULT_404_HTML_PERSISTENT];
+    } else
+#endif
+    {
+      hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = g_psHTTPHeaderStrings[DEFAULT_404_HTML];
+    }
+
+    /* Set up to send the first header string. */
+    hs->hdr_index = 0;
+    hs->hdr_pos = 0;
+    return;
+  }
+  /* We are dealing with a particular filename. Look for one other
+      special case.  We assume that any filename with "404" in it must be
+      indicative of a 404 server error whereas all other files require
+      the 200 OK header. */
+  if (strstr(uri, "404")) {
+    hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_NOT_FOUND];
+  } else if (strstr(uri, "400")) {
+    hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_BAD_REQUEST];
+  } else if (strstr(uri, "501")) {
+    hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_NOT_IMPL];
+  } else {
+    hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_OK];
+  }
+
+  /* Determine if the URI has any variables and, if so, temporarily remove 
+      them. */
+  vars = strchr(uri, '?');
+  if(vars) {
+    *vars = '\0';
+  }
+
+  /* Get a pointer to the file extension.  We find this by looking for the
+      last occurrence of "." in the filename passed. */
+  ext = NULL;
+  tmp = strchr(uri, '.');
+  while (tmp) {
+    ext = tmp + 1;
+    tmp = strchr(ext, '.');
+  }
+  if (ext != NULL) {
+    /* Now determine the content type and add the relevant header for that. */
+    for (content_type = 0; content_type < NUM_HTTP_HEADERS; content_type++) {
+      /* Have we found a matching extension? */
+      if(!lwip_stricmp(g_psHTTPHeaders[content_type].extension, ext)) {
+        break;
+      }
+    }
+  } else {
+    content_type = NUM_HTTP_HEADERS;
+  }
+
+  /* Reinstate the parameter marker if there was one in the original URI. */
+  if (vars) {
+    *vars = '?';
+  }
+
+#if LWIP_HTTPD_OMIT_HEADER_FOR_EXTENSIONLESS_URI
+  /* Does the URL passed have any file extension?  If not, we assume it
+     is a special-case URL used for control state notification and we do
+     not send any HTTP headers with the response. */
+  if (!ext) {
+    /* Force the header index to a value indicating that all headers
+       have already been sent. */
+    hs->hdr_index = NUM_FILE_HDR_STRINGS;
+    return;
+  }
+#endif /* LWIP_HTTPD_OMIT_HEADER_FOR_EXTENSIONLESS_URI */
+  add_content_len = 1;
+  /* Did we find a matching extension? */
+  if(content_type < NUM_HTTP_HEADERS) {
+    /* yes, store it */
+    hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = g_psHTTPHeaders[content_type].content_type;
+  } else if (!ext) {
+    /* no, no extension found -> use binary transfer to prevent the browser adding '.txt' on save */
+    hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = HTTP_HDR_APP;
+  } else {
+    /* No - use the default, plain text file type. */
+    hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = HTTP_HDR_DEFAULT_TYPE;
+  }
+  /* Add content-length header? */
+#if LWIP_HTTPD_SSI
+  if (hs->ssi != NULL) {
+    add_content_len = 0; /* @todo: get maximum file length from SSI */
+  } else
+#endif /* LWIP_HTTPD_SSI */
+  if ((hs->handle == NULL) ||
+      ((hs->handle->flags & (FS_FILE_FLAGS_HEADER_INCLUDED|FS_FILE_FLAGS_HEADER_PERSISTENT)) == FS_FILE_FLAGS_HEADER_INCLUDED)) {
+    add_content_len = 0;
+  }
+  if (add_content_len) {
+    size_t len;
+    lwip_itoa(hs->hdr_content_len, (size_t)LWIP_HTTPD_MAX_CONTENT_LEN_SIZE,
+      hs->handle->len);
+    len = strlen(hs->hdr_content_len);
+    if (len <= LWIP_HTTPD_MAX_CONTENT_LEN_SIZE - LWIP_HTTPD_MAX_CONTENT_LEN_OFFSET) {
+      SMEMCPY(&hs->hdr_content_len[len], CRLF "\0", 3);
+      hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_NR] = hs->hdr_content_len;
+    } else {
+      add_content_len = 0;
+    }
+  }
+#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
+  if (add_content_len) {
+    hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEPALIVE] = g_psHTTPHeaderStrings[HTTP_HDR_KEEPALIVE_LEN];
+  } else {
+    hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEPALIVE] = g_psHTTPHeaderStrings[HTTP_HDR_CONN_CLOSE];
+  }
+#else /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */
+  if (add_content_len) {
+    hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEPALIVE] = g_psHTTPHeaderStrings[HTTP_HDR_CONTENT_LENGTH];
+  }
+#endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */
+
+  /* Set up to send the first header string. */
+  hs->hdr_index = 0;
+  hs->hdr_pos = 0;
+}
+
+/** Sub-function of http_send(): send dynamic headers
+ *
+ * @returns: - HTTP_NO_DATA_TO_SEND: no new data has been enqueued
+ *           - HTTP_DATA_TO_SEND_CONTINUE: continue with sending HTTP body
+ *           - HTTP_DATA_TO_SEND_BREAK: data has been enqueued, headers pending,
+ *                                      so don't send HTTP body yet
+ */
+static u8_t
+http_send_headers(struct tcp_pcb *pcb, struct http_state *hs)
+{
+  err_t err;
+  u16_t len;
+  u8_t data_to_send = HTTP_NO_DATA_TO_SEND;
+  u16_t hdrlen, sendlen;
+
+  /* How much data can we send? */
+  len = tcp_sndbuf(pcb);
+  sendlen = len;
+
+  while(len && (hs->hdr_index < NUM_FILE_HDR_STRINGS) && sendlen) {
+    const void *ptr;
+    u16_t old_sendlen;
+    u8_t apiflags;
+    /* How much do we have to send from the current header? */
+    hdrlen = (u16_t)strlen(hs->hdrs[hs->hdr_index]);
+
+    /* How much of this can we send? */
+    sendlen = (len < (hdrlen - hs->hdr_pos)) ? len : (hdrlen - hs->hdr_pos);
+
+    /* Send this amount of data or as much as we can given memory
+     * constraints. */
+    ptr = (const void *)(hs->hdrs[hs->hdr_index] + hs->hdr_pos);
+    old_sendlen = sendlen;
+    apiflags = HTTP_IS_HDR_VOLATILE(hs, ptr);
+    if (hs->hdr_index == HDR_STRINGS_IDX_CONTENT_LEN_NR) {
+      /* content-length is always volatile */
+      apiflags |= TCP_WRITE_FLAG_COPY;
+    }
+    if (hs->hdr_index < NUM_FILE_HDR_STRINGS - 1) {
+      apiflags |= TCP_WRITE_FLAG_MORE;
+    }
+    err = http_write(pcb, ptr, &sendlen, apiflags);
+    if ((err == ERR_OK) && (old_sendlen != sendlen)) {
+      /* Remember that we added some more data to be transmitted. */
+      data_to_send = HTTP_DATA_TO_SEND_CONTINUE;
+    } else if (err != ERR_OK) {
+       /* special case: http_write does not try to send 1 byte */
+      sendlen = 0;
+    }
+
+    /* Fix up the header position for the next time round. */
+    hs->hdr_pos += sendlen;
+    len -= sendlen;
+
+    /* Have we finished sending this string? */
+    if(hs->hdr_pos == hdrlen) {
+      /* Yes - move on to the next one */
+      hs->hdr_index++;
+      /* skip headers that are NULL (not all headers are required) */
+      while ((hs->hdr_index < NUM_FILE_HDR_STRINGS) &&
+         (hs->hdrs[hs->hdr_index] == NULL)) {
+        hs->hdr_index++;
+      }
+      hs->hdr_pos = 0;
+    }
+  }
+
+  if ((hs->hdr_index >= NUM_FILE_HDR_STRINGS) && (hs->file == NULL)) {
+    /* When we are at the end of the headers, check for data to send
+     * instead of waiting for ACK from remote side to continue
+     * (which would happen when sending files from async read). */
+    if(http_check_eof(pcb, hs)) {
+      data_to_send = HTTP_DATA_TO_SEND_CONTINUE;
+    }
+  }
+  /* If we get here and there are still header bytes to send, we send
+   * the header information we just wrote immediately. If there are no
+   * more headers to send, but we do have file data to send, drop through
+   * to try to send some file data too. */
+  if((hs->hdr_index < NUM_FILE_HDR_STRINGS) || !hs->file) {
+    LWIP_DEBUGF(HTTPD_DEBUG, ("tcp_output\n"));
+    return HTTP_DATA_TO_SEND_BREAK;
+  }
+  return data_to_send;
+}
+#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */
+
+/** Sub-function of http_send(): end-of-file (or block) is reached,
+ * either close the file or read the next block (if supported).
+ *
+ * @returns: 0 if the file is finished or no data has been read
+ *           1 if the file is not finished and data has been read
+ */
+static u8_t
+http_check_eof(struct tcp_pcb *pcb, struct http_state *hs)
+{
+  int bytes_left;
+#if LWIP_HTTPD_DYNAMIC_FILE_READ
+  int count;
+#ifdef HTTPD_MAX_WRITE_LEN
+  int max_write_len;
+#endif /* HTTPD_MAX_WRITE_LEN */
+#endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */
+
+  /* Do we have a valid file handle? */
+  if (hs->handle == NULL) {
+    /* No - close the connection. */
+    http_eof(pcb, hs);
+    return 0;
+  }
+  bytes_left = fs_bytes_left(hs->handle);
+  if (bytes_left <= 0) {
+    /* We reached the end of the file so this request is done. */
+    LWIP_DEBUGF(HTTPD_DEBUG, ("End of file.\n"));
+    http_eof(pcb, hs);
+    return 0;
+  }
+#if LWIP_HTTPD_DYNAMIC_FILE_READ
+  /* Do we already have a send buffer allocated? */
+  if(hs->buf) {
+    /* Yes - get the length of the buffer */
+    count = LWIP_MIN(hs->buf_len, bytes_left);
+  } else {
+    /* We don't have a send buffer so allocate one now */
+    count = tcp_sndbuf(pcb);
+    if(bytes_left < count) {
+      count = bytes_left;
+    }
+#ifdef HTTPD_MAX_WRITE_LEN
+    /* Additional limitation: e.g. don't enqueue more than 2*mss at once */
+    max_write_len = HTTPD_MAX_WRITE_LEN(pcb);
+    if (count > max_write_len) {
+      count = max_write_len;
+    }
+#endif /* HTTPD_MAX_WRITE_LEN */
+    do {
+      hs->buf = (char*)mem_malloc((mem_size_t)count);
+      if (hs->buf != NULL) {
+        hs->buf_len = count;
+        break;
+      }
+      count = count / 2;
+    } while (count > 100);
+
+    /* Did we get a send buffer? If not, return immediately. */
+    if (hs->buf == NULL) {
+      LWIP_DEBUGF(HTTPD_DEBUG, ("No buff\n"));
+      return 0;
+    }
+  }
+
+  /* Read a block of data from the file. */
+  LWIP_DEBUGF(HTTPD_DEBUG, ("Trying to read %d bytes.\n", count));
+
+#if LWIP_HTTPD_FS_ASYNC_READ
+  count = fs_read_async(hs->handle, hs->buf, count, http_continue, hs);
+#else /* LWIP_HTTPD_FS_ASYNC_READ */
+  count = fs_read(hs->handle, hs->buf, count);
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+  if (count < 0) {
+    if (count == FS_READ_DELAYED) {
+      /* Delayed read, wait for FS to unblock us */
+      return 0;
+    }
+    /* We reached the end of the file so this request is done.
+     * @todo: close here for HTTP/1.1 when reading file fails */
+    LWIP_DEBUGF(HTTPD_DEBUG, ("End of file.\n"));
+    http_eof(pcb, hs);
+    return 0;
+  }
+
+  /* Set up to send the block of data we just read */
+  LWIP_DEBUGF(HTTPD_DEBUG, ("Read %d bytes.\n", count));
+  hs->left = count;
+  hs->file = hs->buf;
+#if LWIP_HTTPD_SSI
+  if (hs->ssi) {
+    hs->ssi->parse_left = count;
+    hs->ssi->parsed = hs->buf;
+  }
+#endif /* LWIP_HTTPD_SSI */
+#else /* LWIP_HTTPD_DYNAMIC_FILE_READ */
+  LWIP_ASSERT("SSI and DYNAMIC_HEADERS turned off but eof not reached", 0);
+#endif /* LWIP_HTTPD_SSI || LWIP_HTTPD_DYNAMIC_HEADERS */
+  return 1;
+}
+
+/** Sub-function of http_send(): This is the normal send-routine for non-ssi files
+ *
+ * @returns: - 1: data has been written (so call tcp_ouput)
+ *           - 0: no data has been written (no need to call tcp_output)
+ */
+static u8_t
+http_send_data_nonssi(struct tcp_pcb *pcb, struct http_state *hs)
+{
+  err_t err;
+  u16_t len;
+  u8_t data_to_send = 0;
+
+  /* We are not processing an SHTML file so no tag checking is necessary.
+   * Just send the data as we received it from the file. */
+  len = (u16_t)LWIP_MIN(hs->left, 0xffff);
+
+  err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs));
+  if (err == ERR_OK) {
+    data_to_send = 1;
+    hs->file += len;
+    hs->left -= len;
+  }
+
+  return data_to_send;
+}
+
+#if LWIP_HTTPD_SSI
+/** Sub-function of http_send(): This is the send-routine for ssi files
+ *
+ * @returns: - 1: data has been written (so call tcp_ouput)
+ *           - 0: no data has been written (no need to call tcp_output)
+ */
+static u8_t
+http_send_data_ssi(struct tcp_pcb *pcb, struct http_state *hs)
+{
+  err_t err = ERR_OK;
+  u16_t len;
+  u8_t data_to_send = 0;
+
+  struct http_ssi_state *ssi = hs->ssi;
+  LWIP_ASSERT("ssi != NULL", ssi != NULL);
+  /* We are processing an SHTML file so need to scan for tags and replace
+   * them with insert strings. We need to be careful here since a tag may
+   * straddle the boundary of two blocks read from the file and we may also
+   * have to split the insert string between two tcp_write operations. */
+
+  /* How much data could we send? */
+  len = tcp_sndbuf(pcb);
+
+  /* Do we have remaining data to send before parsing more? */
+  if(ssi->parsed > hs->file) {
+    len = (u16_t)LWIP_MIN(ssi->parsed - hs->file, 0xffff);
+
+    err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs));
+    if (err == ERR_OK) {
+      data_to_send = 1;
+      hs->file += len;
+      hs->left -= len;
+    }
+
+    /* If the send buffer is full, return now. */
+    if(tcp_sndbuf(pcb) == 0) {
+      return data_to_send;
+    }
+  }
+
+  LWIP_DEBUGF(HTTPD_DEBUG, ("State %d, %d left\n", ssi->tag_state, (int)ssi->parse_left));
+
+  /* We have sent all the data that was already parsed so continue parsing
+   * the buffer contents looking for SSI tags. */
+  while((ssi->parse_left) && (err == ERR_OK)) {
+    if (len == 0) {
+      return data_to_send;
+    }
+    switch(ssi->tag_state) {
+      case TAG_NONE:
+        /* We are not currently processing an SSI tag so scan for the
+         * start of the lead-in marker. */
+        if(*ssi->parsed == g_pcTagLeadIn[0]) {
+          /* We found what could be the lead-in for a new tag so change
+           * state appropriately. */
+          ssi->tag_state = TAG_LEADIN;
+          ssi->tag_index = 1;
+#if !LWIP_HTTPD_SSI_INCLUDE_TAG
+          ssi->tag_started = ssi->parsed;
+#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG */
+        }
+
+        /* Move on to the next character in the buffer */
+        ssi->parse_left--;
+        ssi->parsed++;
+        break;
+
+      case TAG_LEADIN:
+        /* We are processing the lead-in marker, looking for the start of
+         * the tag name. */
+
+        /* Have we reached the end of the leadin? */
+        if(ssi->tag_index == LEN_TAG_LEAD_IN) {
+          ssi->tag_index = 0;
+          ssi->tag_state = TAG_FOUND;
+        } else {
+          /* Have we found the next character we expect for the tag leadin? */
+          if(*ssi->parsed == g_pcTagLeadIn[ssi->tag_index]) {
+            /* Yes - move to the next one unless we have found the complete
+             * leadin, in which case we start looking for the tag itself */
+            ssi->tag_index++;
+          } else {
+            /* We found an unexpected character so this is not a tag. Move
+             * back to idle state. */
+            ssi->tag_state = TAG_NONE;
+          }
+
+          /* Move on to the next character in the buffer */
+          ssi->parse_left--;
+          ssi->parsed++;
+        }
+        break;
+
+      case TAG_FOUND:
+        /* We are reading the tag name, looking for the start of the
+         * lead-out marker and removing any whitespace found. */
+
+        /* Remove leading whitespace between the tag leading and the first
+         * tag name character. */
+        if((ssi->tag_index == 0) && ((*ssi->parsed == ' ') ||
+           (*ssi->parsed == '\t') || (*ssi->parsed == '\n') ||
+           (*ssi->parsed == '\r'))) {
+          /* Move on to the next character in the buffer */
+          ssi->parse_left--;
+          ssi->parsed++;
+          break;
+        }
+
+        /* Have we found the end of the tag name? This is signalled by
+         * us finding the first leadout character or whitespace */
+        if((*ssi->parsed == g_pcTagLeadOut[0]) ||
+           (*ssi->parsed == ' ')  || (*ssi->parsed == '\t') ||
+           (*ssi->parsed == '\n') || (*ssi->parsed == '\r')) {
+
+          if(ssi->tag_index == 0) {
+            /* We read a zero length tag so ignore it. */
+            ssi->tag_state = TAG_NONE;
+          } else {
+            /* We read a non-empty tag so go ahead and look for the
+             * leadout string. */
+            ssi->tag_state = TAG_LEADOUT;
+            LWIP_ASSERT("ssi->tag_index <= 0xff", ssi->tag_index <= 0xff);
+            ssi->tag_name_len = (u8_t)ssi->tag_index;
+            ssi->tag_name[ssi->tag_index] = '\0';
+            if(*ssi->parsed == g_pcTagLeadOut[0]) {
+              ssi->tag_index = 1;
+            } else {
+              ssi->tag_index = 0;
+            }
+          }
+        } else {
+          /* This character is part of the tag name so save it */
+          if(ssi->tag_index < LWIP_HTTPD_MAX_TAG_NAME_LEN) {
+            ssi->tag_name[ssi->tag_index++] = *ssi->parsed;
+          } else {
+            /* The tag was too long so ignore it. */
+            ssi->tag_state = TAG_NONE;
+          }
+        }
+
+        /* Move on to the next character in the buffer */
+        ssi->parse_left--;
+        ssi->parsed++;
+
+        break;
+
+      /* We are looking for the end of the lead-out marker. */
+      case TAG_LEADOUT:
+        /* Remove leading whitespace between the tag leading and the first
+         * tag leadout character. */
+        if((ssi->tag_index == 0) && ((*ssi->parsed == ' ') ||
+           (*ssi->parsed == '\t') || (*ssi->parsed == '\n') ||
+           (*ssi->parsed == '\r'))) {
+          /* Move on to the next character in the buffer */
+          ssi->parse_left--;
+          ssi->parsed++;
+          break;
+        }
+
+        /* Have we found the next character we expect for the tag leadout? */
+        if(*ssi->parsed == g_pcTagLeadOut[ssi->tag_index]) {
+          /* Yes - move to the next one unless we have found the complete
+           * leadout, in which case we need to call the client to process
+           * the tag. */
+
+          /* Move on to the next character in the buffer */
+          ssi->parse_left--;
+          ssi->parsed++;
+
+          if(ssi->tag_index == (LEN_TAG_LEAD_OUT - 1)) {
+            /* Call the client to ask for the insert string for the
+             * tag we just found. */
+#if LWIP_HTTPD_SSI_MULTIPART
+            ssi->tag_part = 0; /* start with tag part 0 */
+#endif /* LWIP_HTTPD_SSI_MULTIPART */
+            get_tag_insert(hs);
+
+            /* Next time through, we are going to be sending data
+             * immediately, either the end of the block we start
+             * sending here or the insert string. */
+            ssi->tag_index = 0;
+            ssi->tag_state = TAG_SENDING;
+            ssi->tag_end = ssi->parsed;
+#if !LWIP_HTTPD_SSI_INCLUDE_TAG
+            ssi->parsed = ssi->tag_started;
+#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/
+
+            /* If there is any unsent data in the buffer prior to the
+             * tag, we need to send it now. */
+            if (ssi->tag_end > hs->file) {
+              /* How much of the data can we send? */
+#if LWIP_HTTPD_SSI_INCLUDE_TAG
+              len = (u16_t)LWIP_MIN(ssi->tag_end - hs->file, 0xffff);
+#else /* LWIP_HTTPD_SSI_INCLUDE_TAG*/
+              /* we would include the tag in sending */
+              len = (u16_t)LWIP_MIN(ssi->tag_started - hs->file, 0xffff);
+#endif /* LWIP_HTTPD_SSI_INCLUDE_TAG*/
+
+              err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs));
+              if (err == ERR_OK) {
+                data_to_send = 1;
+#if !LWIP_HTTPD_SSI_INCLUDE_TAG
+                if(ssi->tag_started <= hs->file) {
+                  /* pretend to have sent the tag, too */
+                  len += ssi->tag_end - ssi->tag_started;
+                }
+#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/
+                hs->file += len;
+                hs->left -= len;
+              }
+            }
+          } else {
+            ssi->tag_index++;
+          }
+        } else {
+          /* We found an unexpected character so this is not a tag. Move
+           * back to idle state. */
+          ssi->parse_left--;
+          ssi->parsed++;
+          ssi->tag_state = TAG_NONE;
+        }
+        break;
+
+      /*
+       * We have found a valid tag and are in the process of sending
+       * data as a result of that discovery. We send either remaining data
+       * from the file prior to the insert point or the insert string itself.
+       */
+      case TAG_SENDING:
+        /* Do we have any remaining file data to send from the buffer prior
+         * to the tag? */
+        if(ssi->tag_end > hs->file) {
+          /* How much of the data can we send? */
+#if LWIP_HTTPD_SSI_INCLUDE_TAG
+          len = (u16_t)LWIP_MIN(ssi->tag_end - hs->file, 0xffff);
+#else /* LWIP_HTTPD_SSI_INCLUDE_TAG*/
+          LWIP_ASSERT("hs->started >= hs->file", ssi->tag_started >= hs->file);
+          /* we would include the tag in sending */
+          len = (u16_t)LWIP_MIN(ssi->tag_started - hs->file, 0xffff);
+#endif /* LWIP_HTTPD_SSI_INCLUDE_TAG*/
+          if (len != 0) {
+            err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs));
+          } else {
+            err = ERR_OK;
+          }
+          if (err == ERR_OK) {
+            data_to_send = 1;
+#if !LWIP_HTTPD_SSI_INCLUDE_TAG
+            if(ssi->tag_started <= hs->file) {
+              /* pretend to have sent the tag, too */
+              len += ssi->tag_end - ssi->tag_started;
+            }
+#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/
+            hs->file += len;
+            hs->left -= len;
+          }
+        } else {
+#if LWIP_HTTPD_SSI_MULTIPART
+          if(ssi->tag_index >= ssi->tag_insert_len) {
+            /* Did the last SSIHandler have more to send? */
+            if (ssi->tag_part != HTTPD_LAST_TAG_PART) {
+              /* If so, call it again */
+              ssi->tag_index = 0;
+              get_tag_insert(hs);
+            }
+          }
+#endif /* LWIP_HTTPD_SSI_MULTIPART */
+
+          /* Do we still have insert data left to send? */
+          if(ssi->tag_index < ssi->tag_insert_len) {
+            /* We are sending the insert string itself. How much of the
+             * insert can we send? */
+            len = (ssi->tag_insert_len - ssi->tag_index);
+
+            /* Note that we set the copy flag here since we only have a
+             * single tag insert buffer per connection. If we don't do
+             * this, insert corruption can occur if more than one insert
+             * is processed before we call tcp_output. */
+            err = http_write(pcb, &(ssi->tag_insert[ssi->tag_index]), &len,
+                             HTTP_IS_TAG_VOLATILE(hs));
+            if (err == ERR_OK) {
+              data_to_send = 1;
+              ssi->tag_index += len;
+              /* Don't return here: keep on sending data */
+            }
+          } else {
+#if LWIP_HTTPD_SSI_MULTIPART
+            if (ssi->tag_part == HTTPD_LAST_TAG_PART)
+#endif /* LWIP_HTTPD_SSI_MULTIPART */
+            {
+              /* We have sent all the insert data so go back to looking for
+               * a new tag. */
+              LWIP_DEBUGF(HTTPD_DEBUG, ("Everything sent.\n"));
+              ssi->tag_index = 0;
+              ssi->tag_state = TAG_NONE;
+#if !LWIP_HTTPD_SSI_INCLUDE_TAG
+              ssi->parsed = ssi->tag_end;
+#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/
+            }
+          }
+          break;
+        default:
+          break;
+      }
+    }
+  }
+
+  /* If we drop out of the end of the for loop, this implies we must have
+   * file data to send so send it now. In TAG_SENDING state, we've already
+   * handled this so skip the send if that's the case. */
+  if((ssi->tag_state != TAG_SENDING) && (ssi->parsed > hs->file)) {
+    len = (u16_t)LWIP_MIN(ssi->parsed - hs->file, 0xffff);
+
+    err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs));
+    if (err == ERR_OK) {
+      data_to_send = 1;
+      hs->file += len;
+      hs->left -= len;
+    }
+  }
+  return data_to_send;
+}
+#endif /* LWIP_HTTPD_SSI */
+
+/**
+ * Try to send more data on this pcb.
+ *
+ * @param pcb the pcb to send data
+ * @param hs connection state
+ */
+static u8_t
+http_send(struct tcp_pcb *pcb, struct http_state *hs)
+{
+  u8_t data_to_send = HTTP_NO_DATA_TO_SEND;
+
+  LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_send: pcb=%p hs=%p left=%d\n", (void*)pcb,
+    (void*)hs, hs != NULL ? (int)hs->left : 0));
+
+#if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND
+  if (hs->unrecved_bytes != 0) {
+    return 0;
+  }
+#endif /* LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND */
+
+  /* If we were passed a NULL state structure pointer, ignore the call. */
+  if (hs == NULL) {
+    return 0;
+  }
+
+#if LWIP_HTTPD_FS_ASYNC_READ
+  /* Check if we are allowed to read from this file.
+     (e.g. SSI might want to delay sending until data is available) */
+  if (!fs_is_file_ready(hs->handle, http_continue, hs)) {
+    return 0;
+  }
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+
+#if LWIP_HTTPD_DYNAMIC_HEADERS
+  /* Do we have any more header data to send for this file? */
+  if (hs->hdr_index < NUM_FILE_HDR_STRINGS) {
+    data_to_send = http_send_headers(pcb, hs);
+    if ((data_to_send != HTTP_DATA_TO_SEND_CONTINUE) &&
+        (hs->hdr_index < NUM_FILE_HDR_STRINGS)) {
+      return data_to_send;
+    }
+  }
+#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */
+
+  /* Have we run out of file data to send? If so, we need to read the next
+   * block from the file. */
+  if (hs->left == 0) {
+    if (!http_check_eof(pcb, hs)) {
+      return 0;
+    }
+  }
+
+#if LWIP_HTTPD_SSI
+  if(hs->ssi) {
+    data_to_send = http_send_data_ssi(pcb, hs);
+  } else
+#endif /* LWIP_HTTPD_SSI */
+  {
+    data_to_send = http_send_data_nonssi(pcb, hs);
+  }
+
+  if((hs->left == 0) && (fs_bytes_left(hs->handle) <= 0)) {
+    /* We reached the end of the file so this request is done.
+     * This adds the FIN flag right into the last data segment. */
+    LWIP_DEBUGF(HTTPD_DEBUG, ("End of file.\n"));
+    http_eof(pcb, hs);
+    return 0;
+  }
+  LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("send_data end.\n"));
+  return data_to_send;
+}
+
+#if LWIP_HTTPD_SUPPORT_EXTSTATUS
+/** Initialize a http connection with a file to send for an error message
+ *
+ * @param hs http connection state
+ * @param error_nr HTTP error number
+ * @return ERR_OK if file was found and hs has been initialized correctly
+ *         another err_t otherwise
+ */
+static err_t
+http_find_error_file(struct http_state *hs, u16_t error_nr)
+{
+  const char *uri1, *uri2, *uri3;
+  err_t err;
+
+  if (error_nr == 501) {
+    uri1 = "/501.html";
+    uri2 = "/501.htm";
+    uri3 = "/501.shtml";
+  } else {
+    /* 400 (bad request is the default) */
+    uri1 = "/400.html";
+    uri2 = "/400.htm";
+    uri3 = "/400.shtml";
+  }
+  err = fs_open(&hs->file_handle, uri1);
+  if (err != ERR_OK) {
+    err = fs_open(&hs->file_handle, uri2);
+    if (err != ERR_OK) {
+      err = fs_open(&hs->file_handle, uri3);
+      if (err != ERR_OK) {
+        LWIP_DEBUGF(HTTPD_DEBUG, ("Error page for error %"U16_F" not found\n",
+          error_nr));
+        return ERR_ARG;
+      }
+    }
+  }
+  return http_init_file(hs, &hs->file_handle, 0, NULL, 0, NULL);
+}
+#else /* LWIP_HTTPD_SUPPORT_EXTSTATUS */
+#define http_find_error_file(hs, error_nr) ERR_ARG
+#endif /* LWIP_HTTPD_SUPPORT_EXTSTATUS */
+
+/**
+ * Get the file struct for a 404 error page.
+ * Tries some file names and returns NULL if none found.
+ *
+ * @param uri pointer that receives the actual file name URI
+ * @return file struct for the error page or NULL no matching file was found
+ */
+static struct fs_file *
+http_get_404_file(struct http_state *hs, const char **uri)
+{
+  err_t err;
+
+  *uri = "/404.html";
+  err = fs_open(&hs->file_handle, *uri);
+  if (err != ERR_OK) {
+    /* 404.html doesn't exist. Try 404.htm instead. */
+    *uri = "/404.htm";
+    err = fs_open(&hs->file_handle, *uri);
+    if (err != ERR_OK) {
+      /* 404.htm doesn't exist either. Try 404.shtml instead. */
+      *uri = "/404.shtml";
+      err = fs_open(&hs->file_handle, *uri);
+      if (err != ERR_OK) {
+        /* 404.htm doesn't exist either. Indicate to the caller that it should
+         * send back a default 404 page.
+         */
+        *uri = NULL;
+        return NULL;
+      }
+    }
+  }
+
+  return &hs->file_handle;
+}
+
+#if LWIP_HTTPD_SUPPORT_POST
+static err_t
+http_handle_post_finished(struct http_state *hs)
+{
+#if LWIP_HTTPD_POST_MANUAL_WND
+  /* Prevent multiple calls to httpd_post_finished, since it might have already
+     been called before from httpd_post_data_recved(). */
+  if (hs->post_finished) {
+    return ERR_OK;
+  }
+  hs->post_finished = 1;
+#endif /* LWIP_HTTPD_POST_MANUAL_WND */
+  /* application error or POST finished */
+  /* NULL-terminate the buffer */
+  http_uri_buf[0] = 0;
+  httpd_post_finished(hs, http_uri_buf, LWIP_HTTPD_URI_BUF_LEN);
+  return http_find_file(hs, http_uri_buf, 0);
+}
+
+/** Pass received POST body data to the application and correctly handle
+ * returning a response document or closing the connection.
+ * ATTENTION: The application is responsible for the pbuf now, so don't free it!
+ *
+ * @param hs http connection state
+ * @param p pbuf to pass to the application
+ * @return ERR_OK if passed successfully, another err_t if the response file
+ *         hasn't been found (after POST finished)
+ */
+static err_t
+http_post_rxpbuf(struct http_state *hs, struct pbuf *p)
+{
+  err_t err;
+
+  if (p != NULL) {
+    /* adjust remaining Content-Length */
+    if (hs->post_content_len_left < p->tot_len) {
+      hs->post_content_len_left = 0;
+    } else {
+      hs->post_content_len_left -= p->tot_len;
+    }
+  }
+#if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND
+  /* prevent connection being closed if httpd_post_data_recved() is called nested */
+  hs->unrecved_bytes++;
+#endif
+  err = httpd_post_receive_data(hs, p);
+#if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND
+  hs->unrecved_bytes--;
+#endif
+  if (err != ERR_OK) {
+    /* Ignore remaining content in case of application error */
+    hs->post_content_len_left = 0;
+  }
+  if (hs->post_content_len_left == 0) {
+#if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND
+    if (hs->unrecved_bytes != 0) {
+       return ERR_OK;
+    }
+#endif /* LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND */
+    /* application error or POST finished */
+    return http_handle_post_finished(hs);
+  }
+
+  return ERR_OK;
+}
+
+/** Handle a post request. Called from http_parse_request when method 'POST'
+ * is found.
+ *
+ * @param p The input pbuf (containing the POST header and body).
+ * @param hs The http connection state.
+ * @param data HTTP request (header and part of body) from input pbuf(s).
+ * @param data_len Size of 'data'.
+ * @param uri The HTTP URI parsed from input pbuf(s).
+ * @param uri_end Pointer to the end of 'uri' (here, the rest of the HTTP
+ *                header starts).
+ * @return ERR_OK: POST correctly parsed and accepted by the application.
+ *         ERR_INPROGRESS: POST not completely parsed (no error yet)
+ *         another err_t: Error parsing POST or denied by the application
+ */
+static err_t
+http_post_request(struct pbuf *inp, struct http_state *hs,
+                  char *data, u16_t data_len, char *uri, char *uri_end)
+{
+  err_t err;
+  /* search for end-of-header (first double-CRLF) */
+  char* crlfcrlf = lwip_strnstr(uri_end + 1, CRLF CRLF, data_len - (uri_end + 1 - data));
+
+  if (crlfcrlf != NULL) {
+    /* search for "Content-Length: " */
+#define HTTP_HDR_CONTENT_LEN                "Content-Length: "
+#define HTTP_HDR_CONTENT_LEN_LEN            16
+#define HTTP_HDR_CONTENT_LEN_DIGIT_MAX_LEN  10
+    char *scontent_len = lwip_strnstr(uri_end + 1, HTTP_HDR_CONTENT_LEN, crlfcrlf - (uri_end + 1));
+    if (scontent_len != NULL) {
+      char *scontent_len_end = lwip_strnstr(scontent_len + HTTP_HDR_CONTENT_LEN_LEN, CRLF, HTTP_HDR_CONTENT_LEN_DIGIT_MAX_LEN);
+      if (scontent_len_end != NULL) {
+        int content_len;
+        char *content_len_num = scontent_len + HTTP_HDR_CONTENT_LEN_LEN;
+        content_len = atoi(content_len_num);
+        if (content_len == 0) {
+          /* if atoi returns 0 on error, fix this */
+          if ((content_len_num[0] != '0') || (content_len_num[1] != '\r')) {
+            content_len = -1;
+          }
+        }
+        if (content_len >= 0) {
+          /* adjust length of HTTP header passed to application */
+          const char *hdr_start_after_uri = uri_end + 1;
+          u16_t hdr_len = (u16_t)LWIP_MIN(data_len, crlfcrlf + 4 - data);
+          u16_t hdr_data_len = (u16_t)LWIP_MIN(data_len, crlfcrlf + 4 - hdr_start_after_uri);
+          u8_t post_auto_wnd = 1;
+          http_uri_buf[0] = 0;
+          /* trim http header */
+          *crlfcrlf = 0;
+          err = httpd_post_begin(hs, uri, hdr_start_after_uri, hdr_data_len, content_len,
+            http_uri_buf, LWIP_HTTPD_URI_BUF_LEN, &post_auto_wnd);
+          if (err == ERR_OK) {
+            /* try to pass in data of the first pbuf(s) */
+            struct pbuf *q = inp;
+            u16_t start_offset = hdr_len;
+#if LWIP_HTTPD_POST_MANUAL_WND
+            hs->no_auto_wnd = !post_auto_wnd;
+#endif /* LWIP_HTTPD_POST_MANUAL_WND */
+            /* set the Content-Length to be received for this POST */
+            hs->post_content_len_left = (u32_t)content_len;
+
+            /* get to the pbuf where the body starts */
+            while((q != NULL) && (q->len <= start_offset)) {
+              start_offset -= q->len;
+              q = q->next;
+            }
+            if (q != NULL) {
+              /* hide the remaining HTTP header */
+              pbuf_header(q, -(s16_t)start_offset);
+#if LWIP_HTTPD_POST_MANUAL_WND
+              if (!post_auto_wnd) {
+                /* already tcp_recved() this data... */
+                hs->unrecved_bytes = q->tot_len;
+              }
+#endif /* LWIP_HTTPD_POST_MANUAL_WND */
+              pbuf_ref(q);
+              return http_post_rxpbuf(hs, q);
+            } else if (hs->post_content_len_left == 0) {
+              q = pbuf_alloc(PBUF_RAW, 0, PBUF_REF);
+              return http_post_rxpbuf(hs, q);
+            } else {
+              return ERR_OK;
+            }
+          } else {
+            /* return file passed from application */
+            return http_find_file(hs, http_uri_buf, 0);
+          }
+        } else {
+          LWIP_DEBUGF(HTTPD_DEBUG, ("POST received invalid Content-Length: %s\n",
+            content_len_num));
+          return ERR_ARG;
+        }
+      }
+    }
+    /* If we come here, headers are fully received (double-crlf), but Content-Length
+       was not included. Since this is currently the only supported method, we have
+       to fail in this case! */
+    LWIP_DEBUGF(HTTPD_DEBUG, ("Error when parsing Content-Length\n"));
+    return ERR_ARG;
+  }
+  /* if we come here, the POST is incomplete */
+#if LWIP_HTTPD_SUPPORT_REQUESTLIST
+  return ERR_INPROGRESS;
+#else /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
+  return ERR_ARG;
+#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
+}
+
+#if LWIP_HTTPD_POST_MANUAL_WND
+/** A POST implementation can call this function to update the TCP window.
+ * This can be used to throttle data reception (e.g. when received data is
+ * programmed to flash and data is received faster than programmed).
+ *
+ * @param connection A connection handle passed to httpd_post_begin for which
+ *        httpd_post_finished has *NOT* been called yet!
+ * @param recved_len Length of data received (for window update)
+ */
+void httpd_post_data_recved(void *connection, u16_t recved_len)
+{
+  struct http_state *hs = (struct http_state*)connection;
+  if (hs != NULL) {
+    if (hs->no_auto_wnd) {
+      u16_t len = recved_len;
+      if (hs->unrecved_bytes >= recved_len) {
+        hs->unrecved_bytes -= recved_len;
+      } else {
+        LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_LEVEL_WARNING, ("httpd_post_data_recved: recved_len too big\n"));
+        len = (u16_t)hs->unrecved_bytes;
+        hs->unrecved_bytes = 0;
+      }
+      if (hs->pcb != NULL) {
+        if (len != 0) {
+          tcp_recved(hs->pcb, len);
+        }
+        if ((hs->post_content_len_left == 0) && (hs->unrecved_bytes == 0)) {
+          /* finished handling POST */
+          http_handle_post_finished(hs);
+          http_send(hs->pcb, hs);
+        }
+      }
+    }
+  }
+}
+#endif /* LWIP_HTTPD_POST_MANUAL_WND */
+
+#endif /* LWIP_HTTPD_SUPPORT_POST */
+
+#if LWIP_HTTPD_FS_ASYNC_READ
+/** Try to send more data if file has been blocked before
+ * This is a callback function passed to fs_read_async().
+ */
+static void
+http_continue(void *connection)
+{
+  struct http_state *hs = (struct http_state*)connection;
+  if (hs && (hs->pcb) && (hs->handle)) {
+    LWIP_ASSERT("hs->pcb != NULL", hs->pcb != NULL);
+    LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("httpd_continue: try to send more data\n"));
+    if (http_send(hs->pcb, hs)) {
+      /* If we wrote anything to be sent, go ahead and send it now. */
+      LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("tcp_output\n"));
+      tcp_output(hs->pcb);
+    }
+  }
+}
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+
+/**
+ * When data has been received in the correct state, try to parse it
+ * as a HTTP request.
+ *
+ * @param inp the received pbuf
+ * @param hs the connection state
+ * @param pcb the tcp_pcb which received this packet
+ * @return ERR_OK if request was OK and hs has been initialized correctly
+ *         ERR_INPROGRESS if request was OK so far but not fully received
+ *         another err_t otherwise
+ */
+static err_t
+http_parse_request(struct pbuf *inp, struct http_state *hs, struct tcp_pcb *pcb)
+{
+  char *data;
+  char *crlf;
+  u16_t data_len;
+  struct pbuf *p = inp;
+#if LWIP_HTTPD_SUPPORT_REQUESTLIST
+  u16_t clen;
+#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
+#if LWIP_HTTPD_SUPPORT_POST
+  err_t err;
+#endif /* LWIP_HTTPD_SUPPORT_POST */
+
+  LWIP_UNUSED_ARG(pcb); /* only used for post */
+  LWIP_ASSERT("p != NULL", p != NULL);
+  LWIP_ASSERT("hs != NULL", hs != NULL);
+
+  if ((hs->handle != NULL) || (hs->file != NULL)) {
+    LWIP_DEBUGF(HTTPD_DEBUG, ("Received data while sending a file\n"));
+    /* already sending a file */
+    /* @todo: abort? */
+    return ERR_USE;
+  }
+
+#if LWIP_HTTPD_SUPPORT_REQUESTLIST
+
+  LWIP_DEBUGF(HTTPD_DEBUG, ("Received %"U16_F" bytes\n", p->tot_len));
+
+  /* first check allowed characters in this pbuf? */
+
+  /* enqueue the pbuf */
+  if (hs->req == NULL) {
+    LWIP_DEBUGF(HTTPD_DEBUG, ("First pbuf\n"));
+    hs->req = p;
+  } else {
+    LWIP_DEBUGF(HTTPD_DEBUG, ("pbuf enqueued\n"));
+    pbuf_cat(hs->req, p);
+  }
+  /* increase pbuf ref counter as it is freed when we return but we want to
+     keep it on the req list */
+  pbuf_ref(p);
+
+  if (hs->req->next != NULL) {
+    data_len = LWIP_MIN(hs->req->tot_len, LWIP_HTTPD_MAX_REQ_LENGTH);
+    pbuf_copy_partial(hs->req, httpd_req_buf, data_len, 0);
+    data = httpd_req_buf;
+  } else
+#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
+  {
+    data = (char *)p->payload;
+    data_len = p->len;
+    if (p->len != p->tot_len) {
+      LWIP_DEBUGF(HTTPD_DEBUG, ("Warning: incomplete header due to chained pbufs\n"));
+    }
+  }
+
+  /* received enough data for minimal request? */
+  if (data_len >= MIN_REQ_LEN) {
+    /* wait for CRLF before parsing anything */
+    crlf = lwip_strnstr(data, CRLF, data_len);
+    if (crlf != NULL) {
+#if LWIP_HTTPD_SUPPORT_POST
+      int is_post = 0;
+#endif /* LWIP_HTTPD_SUPPORT_POST */
+      int is_09 = 0;
+      char *sp1, *sp2;
+      u16_t left_len, uri_len;
+      LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("CRLF received, parsing request\n"));
+      /* parse method */
+      if (!strncmp(data, "GET ", 4)) {
+        sp1 = data + 3;
+        /* received GET request */
+        LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Received GET request\"\n"));
+#if LWIP_HTTPD_SUPPORT_POST
+      } else if (!strncmp(data, "POST ", 5)) {
+        /* store request type */
+        is_post = 1;
+        sp1 = data + 4;
+        /* received GET request */
+        LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Received POST request\n"));
+#endif /* LWIP_HTTPD_SUPPORT_POST */
+      } else {
+        /* null-terminate the METHOD (pbuf is freed anyway wen returning) */
+        data[4] = 0;
+        /* unsupported method! */
+        LWIP_DEBUGF(HTTPD_DEBUG, ("Unsupported request method (not implemented): \"%s\"\n",
+          data));
+        return http_find_error_file(hs, 501);
+      }
+      /* if we come here, method is OK, parse URI */
+      left_len = (u16_t)(data_len - ((sp1 +1) - data));
+      sp2 = lwip_strnstr(sp1 + 1, " ", left_len);
+#if LWIP_HTTPD_SUPPORT_V09
+      if (sp2 == NULL) {
+        /* HTTP 0.9: respond with correct protocol version */
+        sp2 = lwip_strnstr(sp1 + 1, CRLF, left_len);
+        is_09 = 1;
+#if LWIP_HTTPD_SUPPORT_POST
+        if (is_post) {
+          /* HTTP/0.9 does not support POST */
+          goto badrequest;
+        }
+#endif /* LWIP_HTTPD_SUPPORT_POST */
+      }
+#endif /* LWIP_HTTPD_SUPPORT_V09 */
+      uri_len = (u16_t)(sp2 - (sp1 + 1));
+      if ((sp2 != 0) && (sp2 > sp1)) {
+        /* wait for CRLFCRLF (indicating end of HTTP headers) before parsing anything */
+        if (lwip_strnstr(data, CRLF CRLF, data_len) != NULL) {
+          char *uri = sp1 + 1;
+#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
+          /* This is HTTP/1.0 compatible: for strict 1.1, a connection
+             would always be persistent unless "close" was specified. */
+          if (!is_09 && (lwip_strnstr(data, HTTP11_CONNECTIONKEEPALIVE, data_len) ||
+              lwip_strnstr(data, HTTP11_CONNECTIONKEEPALIVE2, data_len))) {
+            hs->keepalive = 1;
+          } else {
+            hs->keepalive = 0;
+          }
+#endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */
+          /* null-terminate the METHOD (pbuf is freed anyway wen returning) */
+          *sp1 = 0;
+          uri[uri_len] = 0;
+          LWIP_DEBUGF(HTTPD_DEBUG, ("Received \"%s\" request for URI: \"%s\"\n",
+                      data, uri));
+#if LWIP_HTTPD_SUPPORT_POST
+          if (is_post) {
+#if LWIP_HTTPD_SUPPORT_REQUESTLIST
+            struct pbuf *q = hs->req;
+#else /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
+            struct pbuf *q = inp;
+#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
+            err = http_post_request(q, hs, data, data_len, uri, sp2);
+            if (err != ERR_OK) {
+              /* restore header for next try */
+              *sp1 = ' ';
+              *sp2 = ' ';
+              uri[uri_len] = ' ';
+            }
+            if (err == ERR_ARG) {
+              goto badrequest;
+            }
+            return err;
+          } else
+#endif /* LWIP_HTTPD_SUPPORT_POST */
+          {
+            return http_find_file(hs, uri, is_09);
+          }
+        }
+      } else {
+        LWIP_DEBUGF(HTTPD_DEBUG, ("invalid URI\n"));
+      }
+    }
+  }
+
+#if LWIP_HTTPD_SUPPORT_REQUESTLIST
+  clen = pbuf_clen(hs->req);
+  if ((hs->req->tot_len <= LWIP_HTTPD_REQ_BUFSIZE) &&
+    (clen <= LWIP_HTTPD_REQ_QUEUELEN)) {
+    /* request not fully received (too short or CRLF is missing) */
+    return ERR_INPROGRESS;
+  } else
+#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
+  {
+#if LWIP_HTTPD_SUPPORT_POST
+badrequest:
+#endif /* LWIP_HTTPD_SUPPORT_POST */
+    LWIP_DEBUGF(HTTPD_DEBUG, ("bad request\n"));
+    /* could not parse request */
+    return http_find_error_file(hs, 400);
+  }
+}
+
+/** Try to find the file specified by uri and, if found, initialize hs
+ * accordingly.
+ *
+ * @param hs the connection state
+ * @param uri the HTTP header URI
+ * @param is_09 1 if the request is HTTP/0.9 (no HTTP headers in response)
+ * @return ERR_OK if file was found and hs has been initialized correctly
+ *         another err_t otherwise
+ */
+static err_t
+http_find_file(struct http_state *hs, const char *uri, int is_09)
+{
+  size_t loop;
+  struct fs_file *file = NULL;
+  char *params = NULL;
+  err_t err;
+#if LWIP_HTTPD_CGI
+  int i;
+#endif /* LWIP_HTTPD_CGI */
+#if !LWIP_HTTPD_SSI
+  const
+#endif /* !LWIP_HTTPD_SSI */
+  /* By default, assume we will not be processing server-side-includes tags */
+  u8_t tag_check = 0;
+
+  /* Have we been asked for the default file (in root or a directory) ? */
+#if LWIP_HTTPD_MAX_REQUEST_URI_LEN
+  size_t uri_len = strlen(uri);
+  if ((uri_len > 0) && (uri[uri_len-1] == '/') &&
+      ((uri != http_uri_buf) || (uri_len == 1))) {
+    size_t copy_len = LWIP_MIN(sizeof(http_uri_buf) - 1, uri_len - 1);
+    if (copy_len > 0) {
+      MEMCPY(http_uri_buf, uri, copy_len);
+      http_uri_buf[copy_len] = 0;
+    }
+#else /* LWIP_HTTPD_MAX_REQUEST_URI_LEN */
+  if ((uri[0] == '/') &&  (uri[1] == 0)) {
+#endif /* LWIP_HTTPD_MAX_REQUEST_URI_LEN */
+    /* Try each of the configured default filenames until we find one
+       that exists. */
+    for (loop = 0; loop < NUM_DEFAULT_FILENAMES; loop++) {
+      const char* file_name;
+#if LWIP_HTTPD_MAX_REQUEST_URI_LEN
+      if (copy_len > 0) {
+        size_t len_left = sizeof(http_uri_buf) - copy_len - 1;
+        if (len_left > 0) {
+          size_t name_len = strlen(g_psDefaultFilenames[loop].name);
+          size_t name_copy_len = LWIP_MIN(len_left, name_len);
+          MEMCPY(&http_uri_buf[copy_len], g_psDefaultFilenames[loop].name, name_copy_len);
+        }
+        file_name = http_uri_buf;
+      } else
+#endif /* LWIP_HTTPD_MAX_REQUEST_URI_LEN */
+      {
+        file_name = g_psDefaultFilenames[loop].name;
+      }
+      LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Looking for %s...\n", file_name));
+      err = fs_open(&hs->file_handle, file_name);
+      if(err == ERR_OK) {
+        uri = file_name;
+        file = &hs->file_handle;
+        LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Opened.\n"));
+#if LWIP_HTTPD_SSI
+        tag_check = g_psDefaultFilenames[loop].shtml;
+#endif /* LWIP_HTTPD_SSI */
+        break;
+      }
+    }
+  }
+  if (file == NULL) {
+    /* No - we've been asked for a specific file. */
+    /* First, isolate the base URI (without any parameters) */
+    params = (char *)strchr(uri, '?');
+    if (params != NULL) {
+      /* URI contains parameters. NULL-terminate the base URI */
+      *params = '\0';
+      params++;
+    }
+
+#if LWIP_HTTPD_CGI
+    http_cgi_paramcount = -1;
+    /* Does the base URI we have isolated correspond to a CGI handler? */
+    if (g_iNumCGIs && g_pCGIs) {
+      for (i = 0; i < g_iNumCGIs; i++) {
+        if (strcmp(uri, g_pCGIs[i].pcCGIName) == 0) {
+          /*
+           * We found a CGI that handles this URI so extract the
+           * parameters and call the handler.
+           */
+           http_cgi_paramcount = extract_uri_parameters(hs, params);
+           uri = g_pCGIs[i].pfnCGIHandler(i, http_cgi_paramcount, hs->params,
+                                          hs->param_vals);
+           break;
+        }
+      }
+    }
+#endif /* LWIP_HTTPD_CGI */
+
+    LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Opening %s\n", uri));
+
+    err = fs_open(&hs->file_handle, uri);
+    if (err == ERR_OK) {
+       file = &hs->file_handle;
+    } else {
+      file = http_get_404_file(hs, &uri);
+    }
+#if LWIP_HTTPD_SSI
+    if (file != NULL) {
+      /* See if we have been asked for an shtml file and, if so,
+         enable tag checking. */
+      const char* ext = NULL, *sub;
+      char* param = (char*)strstr(uri, "?");
+      if (param != NULL) {
+         /* separate uri from parameters for now, set back later */
+         *param = 0;
+      }
+      sub = uri;
+      ext = uri;
+      for (sub = strstr(sub, "."); sub != NULL; sub = strstr(sub, "."))
+      {
+         ext = sub;
+         sub++;
+      }
+      tag_check = 0;
+      for (loop = 0; loop < NUM_SHTML_EXTENSIONS; loop++) {
+        if (!lwip_stricmp(ext, g_pcSSIExtensions[loop])) {
+          tag_check = 1;
+          break;
+        }
+      }
+      if (param != NULL) {
+         *param = '?';
+      }
+    }
+#endif /* LWIP_HTTPD_SSI */
+  }
+  if (file == NULL) {
+    /* None of the default filenames exist so send back a 404 page */
+    file = http_get_404_file(hs, &uri);
+  }
+  return http_init_file(hs, file, is_09, uri, tag_check, params);
+}
+
+/** Initialize a http connection with a file to send (if found).
+ * Called by http_find_file and http_find_error_file.
+ *
+ * @param hs http connection state
+ * @param file file structure to send (or NULL if not found)
+ * @param is_09 1 if the request is HTTP/0.9 (no HTTP headers in response)
+ * @param uri the HTTP header URI
+ * @param tag_check enable SSI tag checking
+ * @param params != NULL if URI has parameters (separated by '?')
+ * @return ERR_OK if file was found and hs has been initialized correctly
+ *         another err_t otherwise
+ */
+static err_t
+http_init_file(struct http_state *hs, struct fs_file *file, int is_09, const char *uri,
+               u8_t tag_check, char* params)
+{
+  if (file != NULL) {
+    /* file opened, initialise struct http_state */
+#if LWIP_HTTPD_SSI
+    if (tag_check) {
+      struct http_ssi_state *ssi = http_ssi_state_alloc();
+      if (ssi != NULL) {
+        ssi->tag_index = 0;
+        ssi->tag_state = TAG_NONE;
+        ssi->parsed = file->data;
+        ssi->parse_left = file->len;
+        ssi->tag_end = file->data;
+        hs->ssi = ssi;
+      }
+    }
+#else /* LWIP_HTTPD_SSI */
+    LWIP_UNUSED_ARG(tag_check);
+#endif /* LWIP_HTTPD_SSI */
+    hs->handle = file;
+    hs->file = file->data;
+    LWIP_ASSERT("File length must be positive!", (file->len >= 0));
+#if LWIP_HTTPD_CUSTOM_FILES
+    if (file->is_custom_file && (file->data == NULL)) {
+      /* custom file, need to read data first (via fs_read_custom) */
+      hs->left = 0;
+    } else
+#endif /* LWIP_HTTPD_CUSTOM_FILES */
+    {
+      hs->left = file->len;
+    }
+    hs->retries = 0;
+#if LWIP_HTTPD_TIMING
+    hs->time_started = sys_now();
+#endif /* LWIP_HTTPD_TIMING */
+#if !LWIP_HTTPD_DYNAMIC_HEADERS
+    LWIP_ASSERT("HTTP headers not included in file system",
+       (hs->handle->flags & FS_FILE_FLAGS_HEADER_INCLUDED) != 0);
+#endif /* !LWIP_HTTPD_DYNAMIC_HEADERS */
+#if LWIP_HTTPD_SUPPORT_V09
+    if (is_09 && ((hs->handle->flags & FS_FILE_FLAGS_HEADER_INCLUDED) != 0)) {
+      /* HTTP/0.9 responses are sent without HTTP header,
+         search for the end of the header. */
+      char *file_start = lwip_strnstr(hs->file, CRLF CRLF, hs->left);
+      if (file_start != NULL) {
+        size_t diff = file_start + 4 - hs->file;
+        hs->file += diff;
+        hs->left -= (u32_t)diff;
+      }
+    }
+#endif /* LWIP_HTTPD_SUPPORT_V09*/
+#if LWIP_HTTPD_CGI_SSI
+    if (params != NULL) {
+      /* URI contains parameters, call generic CGI handler */
+      int count;
+#if LWIP_HTTPD_CGI
+      if (http_cgi_paramcount >= 0) {
+        count = http_cgi_paramcount;
+      } else
+#endif
+      {
+        count = extract_uri_parameters(hs, params);
+      }
+      httpd_cgi_handler(uri, count, http_cgi_params, http_cgi_param_vals
+#if defined(LWIP_HTTPD_FILE_STATE) && LWIP_HTTPD_FILE_STATE
+         , hs->handle->state
+#endif /* LWIP_HTTPD_FILE_STATE */
+                        );
+    }
+#else /* LWIP_HTTPD_CGI_SSI */
+    LWIP_UNUSED_ARG(params);
+#endif /* LWIP_HTTPD_CGI_SSI */
+  } else {
+    hs->handle = NULL;
+    hs->file = NULL;
+    hs->left = 0;
+    hs->retries = 0;
+  }
+#if LWIP_HTTPD_DYNAMIC_HEADERS
+  /* Determine the HTTP headers to send based on the file extension of
+   * the requested URI. */
+  if ((hs->handle == NULL) || ((hs->handle->flags & FS_FILE_FLAGS_HEADER_INCLUDED) == 0)) {
+    get_http_headers(hs, uri);
+  }
+#else /* LWIP_HTTPD_DYNAMIC_HEADERS */
+  LWIP_UNUSED_ARG(uri);
+#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */
+#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
+  if (hs->keepalive) {
+#if LWIP_HTTPD_SSI
+     if (hs->ssi != NULL) {
+       hs->keepalive = 0;
+     } else
+#endif /* LWIP_HTTPD_SSI */
+     {
+       if ((hs->handle != NULL) &&
+           ((hs->handle->flags & (FS_FILE_FLAGS_HEADER_INCLUDED|FS_FILE_FLAGS_HEADER_PERSISTENT)) == FS_FILE_FLAGS_HEADER_INCLUDED)) {
+         hs->keepalive = 0;
+       }
+     }
+  }
+#endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */
+  return ERR_OK;
+}
+
+/**
+ * The pcb had an error and is already deallocated.
+ * The argument might still be valid (if != NULL).
+ */
+static void
+http_err(void *arg, err_t err)
+{
+  struct http_state *hs = (struct http_state *)arg;
+  LWIP_UNUSED_ARG(err);
+
+  LWIP_DEBUGF(HTTPD_DEBUG, ("http_err: %s", lwip_strerr(err)));
+
+  if (hs != NULL) {
+    http_state_free(hs);
+  }
+}
+
+/**
+ * Data has been sent and acknowledged by the remote host.
+ * This means that more data can be sent.
+ */
+static err_t
+http_sent(void *arg, struct tcp_pcb *pcb, u16_t len)
+{
+  struct http_state *hs = (struct http_state *)arg;
+
+  LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_sent %p\n", (void*)pcb));
+
+  LWIP_UNUSED_ARG(len);
+
+  if (hs == NULL) {
+    return ERR_OK;
+  }
+
+  hs->retries = 0;
+
+  http_send(pcb, hs);
+
+  return ERR_OK;
+}
+
+/**
+ * The poll function is called every 2nd second.
+ * If there has been no data sent (which resets the retries) in 8 seconds, close.
+ * If the last portion of a file has not been sent in 2 seconds, close.
+ *
+ * This could be increased, but we don't want to waste resources for bad connections.
+ */
+static err_t
+http_poll(void *arg, struct tcp_pcb *pcb)
+{
+  struct http_state *hs = (struct http_state *)arg;
+  LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_poll: pcb=%p hs=%p pcb_state=%s\n",
+    (void*)pcb, (void*)hs, tcp_debug_state_str(pcb->state)));
+
+  if (hs == NULL) {
+    err_t closed;
+    /* arg is null, close. */
+    LWIP_DEBUGF(HTTPD_DEBUG, ("http_poll: arg is NULL, close\n"));
+    closed = http_close_conn(pcb, NULL);
+    LWIP_UNUSED_ARG(closed);
+#if LWIP_HTTPD_ABORT_ON_CLOSE_MEM_ERROR
+    if (closed == ERR_MEM) {
+       tcp_abort(pcb);
+       return ERR_ABRT;
+    }
+#endif /* LWIP_HTTPD_ABORT_ON_CLOSE_MEM_ERROR */
+    return ERR_OK;
+  } else {
+    hs->retries++;
+    if (hs->retries == HTTPD_MAX_RETRIES) {
+      LWIP_DEBUGF(HTTPD_DEBUG, ("http_poll: too many retries, close\n"));
+      http_close_conn(pcb, hs);
+      return ERR_OK;
+    }
+
+    /* If this connection has a file open, try to send some more data. If
+     * it has not yet received a GET request, don't do this since it will
+     * cause the connection to close immediately. */
+    if(hs && (hs->handle)) {
+      LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_poll: try to send more data\n"));
+      if(http_send(pcb, hs)) {
+        /* If we wrote anything to be sent, go ahead and send it now. */
+        LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("tcp_output\n"));
+        tcp_output(pcb);
+      }
+    }
+  }
+
+  return ERR_OK;
+}
+
+/**
+ * Data has been received on this pcb.
+ * For HTTP 1.0, this should normally only happen once (if the request fits in one packet).
+ */
+static err_t
+http_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
+{
+  struct http_state *hs = (struct http_state *)arg;
+  LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_recv: pcb=%p pbuf=%p err=%s\n", (void*)pcb,
+    (void*)p, lwip_strerr(err)));
+
+  if ((err != ERR_OK) || (p == NULL) || (hs == NULL)) {
+    /* error or closed by other side? */
+    if (p != NULL) {
+      /* Inform TCP that we have taken the data. */
+      tcp_recved(pcb, p->tot_len);
+      pbuf_free(p);
+    }
+    if (hs == NULL) {
+      /* this should not happen, only to be robust */
+      LWIP_DEBUGF(HTTPD_DEBUG, ("Error, http_recv: hs is NULL, close\n"));
+    }
+    http_close_conn(pcb, hs);
+    return ERR_OK;
+  }
+
+#if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND
+  if (hs->no_auto_wnd) {
+     hs->unrecved_bytes += p->tot_len;
+  } else
+#endif /* LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND */
+  {
+    /* Inform TCP that we have taken the data. */
+    tcp_recved(pcb, p->tot_len);
+  }
+
+#if LWIP_HTTPD_SUPPORT_POST
+  if (hs->post_content_len_left > 0) {
+    /* reset idle counter when POST data is received */
+    hs->retries = 0;
+    /* this is data for a POST, pass the complete pbuf to the application */
+    http_post_rxpbuf(hs, p);
+    /* pbuf is passed to the application, don't free it! */
+    if (hs->post_content_len_left == 0) {
+      /* all data received, send response or close connection */
+      http_send(pcb, hs);
+    }
+    return ERR_OK;
+  } else
+#endif /* LWIP_HTTPD_SUPPORT_POST */
+  {
+    if (hs->handle == NULL) {
+      err_t parsed = http_parse_request(p, hs, pcb);
+      LWIP_ASSERT("http_parse_request: unexpected return value", parsed == ERR_OK
+        || parsed == ERR_INPROGRESS ||parsed == ERR_ARG || parsed == ERR_USE);
+#if LWIP_HTTPD_SUPPORT_REQUESTLIST
+      if (parsed != ERR_INPROGRESS) {
+        /* request fully parsed or error */
+        if (hs->req != NULL) {
+          pbuf_free(hs->req);
+          hs->req = NULL;
+        }
+      }
+#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
+      pbuf_free(p);
+      if (parsed == ERR_OK) {
+#if LWIP_HTTPD_SUPPORT_POST
+       if (hs->post_content_len_left == 0)
+#endif /* LWIP_HTTPD_SUPPORT_POST */
+        {
+          LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_recv: data %p len %"S32_F"\n", (const void*)hs->file, hs->left));
+          http_send(pcb, hs);
+        }
+      } else if (parsed == ERR_ARG) {
+        /* @todo: close on ERR_USE? */
+        http_close_conn(pcb, hs);
+      }
+    } else {
+      LWIP_DEBUGF(HTTPD_DEBUG, ("http_recv: already sending data\n"));
+      /* already sending but still receiving data, we might want to RST here? */
+      pbuf_free(p);
+    }
+  }
+  return ERR_OK;
+}
+
+/**
+ * A new incoming connection has been accepted.
+ */
+static err_t
+http_accept(void *arg, struct tcp_pcb *pcb, err_t err)
+{
+  struct http_state *hs;
+  LWIP_UNUSED_ARG(err);
+  LWIP_UNUSED_ARG(arg);
+  LWIP_DEBUGF(HTTPD_DEBUG, ("http_accept %p / %p\n", (void*)pcb, arg));
+
+  if ((err != ERR_OK) || (pcb == NULL)) {
+    return ERR_VAL;
+  }
+
+  /* Set priority */
+  tcp_setprio(pcb, HTTPD_TCP_PRIO);
+
+  /* Allocate memory for the structure that holds the state of the
+     connection - initialized by that function. */
+  hs = http_state_alloc();
+  if (hs == NULL) {
+    LWIP_DEBUGF(HTTPD_DEBUG, ("http_accept: Out of memory, RST\n"));
+    return ERR_MEM;
+  }
+  hs->pcb = pcb;
+
+  /* Tell TCP that this is the structure we wish to be passed for our
+     callbacks. */
+  tcp_arg(pcb, hs);
+
+  /* Set up the various callback functions */
+  tcp_recv(pcb, http_recv);
+  tcp_err(pcb, http_err);
+  tcp_poll(pcb, http_poll, HTTPD_POLL_INTERVAL);
+  tcp_sent(pcb, http_sent);
+
+  return ERR_OK;
+}
+
+/**
+ * @ingroup httpd
+ * Initialize the httpd: set up a listening PCB and bind it to the defined port
+ */
+void
+httpd_init(void)
+{
+  struct tcp_pcb *pcb;
+  err_t err;
+
+#if HTTPD_USE_MEM_POOL
+  LWIP_MEMPOOL_INIT(HTTPD_STATE);
+#if LWIP_HTTPD_SSI
+  LWIP_MEMPOOL_INIT(HTTPD_SSI_STATE);
+#endif
+#endif
+  LWIP_DEBUGF(HTTPD_DEBUG, ("httpd_init\n"));
+
+  pcb = tcp_new_ip_type(IPADDR_TYPE_ANY);
+  LWIP_ASSERT("httpd_init: tcp_new failed", pcb != NULL);
+  tcp_setprio(pcb, HTTPD_TCP_PRIO);
+  /* set SOF_REUSEADDR here to explicitly bind httpd to multiple interfaces */
+  err = tcp_bind(pcb, IP_ANY_TYPE, HTTPD_SERVER_PORT);
+  LWIP_UNUSED_ARG(err); /* in case of LWIP_NOASSERT */
+  LWIP_ASSERT("httpd_init: tcp_bind failed", err == ERR_OK);
+  pcb = tcp_listen(pcb);
+  LWIP_ASSERT("httpd_init: tcp_listen failed", pcb != NULL);
+  tcp_accept(pcb, http_accept);
+}
+
+#if LWIP_HTTPD_SSI
+/**
+ * Set the SSI handler function.
+ *
+ * @param ssi_handler the SSI handler function
+ * @param tags an array of SSI tag strings to search for in SSI-enabled files
+ * @param num_tags number of tags in the 'tags' array
+ */
+void
+http_set_ssi_handler(tSSIHandler ssi_handler, const char **tags, int num_tags)
+{
+  LWIP_DEBUGF(HTTPD_DEBUG, ("http_set_ssi_handler\n"));
+
+  LWIP_ASSERT("no ssi_handler given", ssi_handler != NULL);
+  g_pfnSSIHandler = ssi_handler;
+
+#if LWIP_HTTPD_SSI_RAW
+  LWIP_UNUSED_ARG(tags);
+  LWIP_UNUSED_ARG(num_tags);
+#else /* LWIP_HTTPD_SSI_RAW */
+  LWIP_ASSERT("no tags given", tags != NULL);
+  LWIP_ASSERT("invalid number of tags", num_tags > 0);
+
+  g_ppcTags = tags;
+  g_iNumTags = num_tags;
+#endif /* !LWIP_HTTPD_SSI_RAW */
+}
+#endif /* LWIP_HTTPD_SSI */
+
+#if LWIP_HTTPD_CGI
+/**
+ * Set an array of CGI filenames/handler functions
+ *
+ * @param cgis an array of CGI filenames/handler functions
+ * @param num_handlers number of elements in the 'cgis' array
+ */
+void
+http_set_cgi_handlers(const tCGI *cgis, int num_handlers)
+{
+  LWIP_ASSERT("no cgis given", cgis != NULL);
+  LWIP_ASSERT("invalid number of handlers", num_handlers > 0);
+
+  g_pCGIs = cgis;
+  g_iNumCGIs = num_handlers;
+}
+#endif /* LWIP_HTTPD_CGI */
+
+#endif /* LWIP_TCP && LWIP_CALLBACK_API */

+ 114 - 0
libs/thirdparty/LwIP/src/apps/httpd/httpd_structs.h

@@ -0,0 +1,114 @@
+#ifndef LWIP_HTTPD_STRUCTS_H
+#define LWIP_HTTPD_STRUCTS_H
+
+#include "lwip/apps/httpd.h"
+
+#if LWIP_HTTPD_DYNAMIC_HEADERS
+/** This struct is used for a list of HTTP header strings for various
+ * filename extensions. */
+typedef struct
+{
+  const char *extension;
+  const char *content_type;
+} tHTTPHeader;
+
+/** A list of strings used in HTTP headers (see RFC 1945 HTTP/1.0 and
+ * RFC 2616 HTTP/1.1 for header field definitions) */
+static const char * const g_psHTTPHeaderStrings[] =
+{
+ "HTTP/1.0 200 OK\r\n",
+ "HTTP/1.0 404 File not found\r\n",
+ "HTTP/1.0 400 Bad Request\r\n",
+ "HTTP/1.0 501 Not Implemented\r\n",
+ "HTTP/1.1 200 OK\r\n",
+ "HTTP/1.1 404 File not found\r\n",
+ "HTTP/1.1 400 Bad Request\r\n",
+ "HTTP/1.1 501 Not Implemented\r\n",
+ "Content-Length: ",
+ "Connection: Close\r\n",
+ "Connection: keep-alive\r\n",
+ "Connection: keep-alive\r\nContent-Length: ",
+ "Server: "HTTPD_SERVER_AGENT"\r\n",
+ "\r\n<html><body><h2>404: The requested file cannot be found.</h2></body></html>\r\n"
+#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
+ ,"Connection: keep-alive\r\nContent-Length: 77\r\n\r\n<html><body><h2>404: The requested file cannot be found.</h2></body></html>\r\n"
+#endif
+};
+
+/* Indexes into the g_psHTTPHeaderStrings array */
+#define HTTP_HDR_OK             0 /* 200 OK */
+#define HTTP_HDR_NOT_FOUND      1 /* 404 File not found */
+#define HTTP_HDR_BAD_REQUEST    2 /* 400 Bad request */
+#define HTTP_HDR_NOT_IMPL       3 /* 501 Not Implemented */
+#define HTTP_HDR_OK_11          4 /* 200 OK */
+#define HTTP_HDR_NOT_FOUND_11   5 /* 404 File not found */
+#define HTTP_HDR_BAD_REQUEST_11 6 /* 400 Bad request */
+#define HTTP_HDR_NOT_IMPL_11    7 /* 501 Not Implemented */
+#define HTTP_HDR_CONTENT_LENGTH 8 /* Content-Length: (HTTP 1.0)*/
+#define HTTP_HDR_CONN_CLOSE     9 /* Connection: Close (HTTP 1.1) */
+#define HTTP_HDR_CONN_KEEPALIVE 10 /* Connection: keep-alive (HTTP 1.1) */
+#define HTTP_HDR_KEEPALIVE_LEN  11 /* Connection: keep-alive + Content-Length: (HTTP 1.1)*/
+#define HTTP_HDR_SERVER         12 /* Server: HTTPD_SERVER_AGENT */
+#define DEFAULT_404_HTML        13 /* default 404 body */
+#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
+#define DEFAULT_404_HTML_PERSISTENT 14 /* default 404 body, but including Connection: keep-alive */
+#endif
+
+
+#define HTTP_HDR_HTML           "Content-type: text/html\r\n\r\n"
+#define HTTP_HDR_SSI            "Content-type: text/html\r\nExpires: Fri, 10 Apr 2008 14:00:00 GMT\r\nPragma: no-cache\r\n\r\n"
+#define HTTP_HDR_GIF            "Content-type: image/gif\r\n\r\n"
+#define HTTP_HDR_PNG            "Content-type: image/png\r\n\r\n"
+#define HTTP_HDR_JPG            "Content-type: image/jpeg\r\n\r\n"
+#define HTTP_HDR_BMP            "Content-type: image/bmp\r\n\r\n"
+#define HTTP_HDR_ICO            "Content-type: image/x-icon\r\n\r\n"
+#define HTTP_HDR_APP            "Content-type: application/octet-stream\r\n\r\n"
+#define HTTP_HDR_JS             "Content-type: application/javascript\r\n\r\n"
+#define HTTP_HDR_RA             "Content-type: application/javascript\r\n\r\n"
+#define HTTP_HDR_CSS            "Content-type: text/css\r\n\r\n"
+#define HTTP_HDR_SWF            "Content-type: application/x-shockwave-flash\r\n\r\n"
+#define HTTP_HDR_XML            "Content-type: text/xml\r\n\r\n"
+#define HTTP_HDR_PDF            "Content-type: application/pdf\r\n\r\n"
+#define HTTP_HDR_JSON           "Content-type: application/json\r\n\r\n"
+
+#define HTTP_HDR_DEFAULT_TYPE   "Content-type: text/plain\r\n\r\n"
+
+/** A list of extension-to-HTTP header strings (see outdated RFC 1700 MEDIA TYPES
+ * and http://www.iana.org/assignments/media-types for registered content types
+ * and subtypes) */
+static const tHTTPHeader g_psHTTPHeaders[] =
+{
+ { "html", HTTP_HDR_HTML},
+ { "htm",  HTTP_HDR_HTML},
+ { "shtml",HTTP_HDR_SSI},
+ { "shtm", HTTP_HDR_SSI},
+ { "ssi",  HTTP_HDR_SSI},
+ { "gif",  HTTP_HDR_GIF},
+ { "png",  HTTP_HDR_PNG},
+ { "jpg",  HTTP_HDR_JPG},
+ { "bmp",  HTTP_HDR_BMP},
+ { "ico",  HTTP_HDR_ICO},
+ { "class",HTTP_HDR_APP},
+ { "cls",  HTTP_HDR_APP},
+ { "js",   HTTP_HDR_JS},
+ { "ram",  HTTP_HDR_RA},
+ { "css",  HTTP_HDR_CSS},
+ { "swf",  HTTP_HDR_SWF},
+ { "xml",  HTTP_HDR_XML},
+ { "xsl",  HTTP_HDR_XML},
+ { "pdf",  HTTP_HDR_PDF},
+ { "json", HTTP_HDR_JSON}
+};
+
+#define NUM_HTTP_HEADERS (sizeof(g_psHTTPHeaders) / sizeof(tHTTPHeader))
+
+#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */
+
+#if LWIP_HTTPD_SSI
+static const char * const g_pcSSIExtensions[] = {
+  ".shtml", ".shtm", ".ssi", ".xml"
+};
+#define NUM_SHTML_EXTENSIONS (sizeof(g_pcSSIExtensions) / sizeof(const char *))
+#endif /* LWIP_HTTPD_SSI */
+
+#endif /* LWIP_HTTPD_STRUCTS_H */

+ 97 - 97
libs/thirdparty/lwip_2.1.2/src/apps/http/makefsdata/makefsdata → libs/thirdparty/LwIP/src/apps/httpd/makefsdata/makefsdata

@@ -1,97 +1,97 @@
-#!/usr/bin/perl
-
-open(OUTPUT, "> fsdata.c");
-
-chdir("fs");
-open(FILES, "find . -type f |");
-
-while($file = <FILES>) {
-
-    # Do not include files in CVS directories nor backup files.
-    if($file =~ /(CVS|~)/) {
-    	next;
-    }
-    
-    chop($file);
-    
-    open(HEADER, "> /tmp/header") || die $!;
-    if($file =~ /404/) {
-	print(HEADER "HTTP/1.0 404 File not found\r\n");
-    } else {
-	print(HEADER "HTTP/1.0 200 OK\r\n");
-    }
-    print(HEADER "Server: lwIP/pre-0.6 (http://www.sics.se/~adam/lwip/)\r\n");
-    if($file =~ /\.html$/) {
-	print(HEADER "Content-type: text/html\r\n");
-    } elsif($file =~ /\.gif$/) {
-	print(HEADER "Content-type: image/gif\r\n");
-    } elsif($file =~ /\.png$/) {
-	print(HEADER "Content-type: image/png\r\n");
-    } elsif($file =~ /\.jpg$/) {
-	print(HEADER "Content-type: image/jpeg\r\n");
-    } elsif($file =~ /\.class$/) {
-	print(HEADER "Content-type: application/octet-stream\r\n");
-    } elsif($file =~ /\.ram$/) {
-	print(HEADER "Content-type: audio/x-pn-realaudio\r\n");    
-    } else {
-	print(HEADER "Content-type: text/plain\r\n");
-    }
-    print(HEADER "\r\n");
-    close(HEADER);
-
-    unless($file =~ /\.plain$/ || $file =~ /cgi/) {
-	system("cat /tmp/header $file > /tmp/file");
-    } else {
-	system("cp $file /tmp/file");
-    }
-    
-    open(FILE, "/tmp/file");
-    unlink("/tmp/file");
-    unlink("/tmp/header");
-
-    $file =~ s/\.//;
-    $fvar = $file;
-    $fvar =~ s-/-_-g;
-    $fvar =~ s-\.-_-g;
-    print(OUTPUT "static const unsigned char data".$fvar."[] = {\n");
-    print(OUTPUT "\t/* $file */\n\t");
-    for($j = 0; $j < length($file); $j++) {
-	printf(OUTPUT "%#02x, ", unpack("C", substr($file, $j, 1)));
-    }
-    printf(OUTPUT "0,\n");
-    
-    
-    $i = 0;
-    while(read(FILE, $data, 1)) {
-        if($i == 0) {
-            print(OUTPUT "\t");
-        }
-        printf(OUTPUT "%#02x, ", unpack("C", $data));
-        $i++;
-        if($i == 10) {
-            print(OUTPUT "\n");
-            $i = 0;
-        }
-    }
-    print(OUTPUT "};\n\n");
-    close(FILE);
-    push(@fvars, $fvar);
-    push(@files, $file);
-}
-
-for($i = 0; $i < @fvars; $i++) {
-    $file = $files[$i];
-    $fvar = $fvars[$i];
-
-    if($i == 0) {
-        $prevfile = "NULL";
-    } else {
-        $prevfile = "file" . $fvars[$i - 1];
-    }
-    print(OUTPUT "const struct fsdata_file file".$fvar."[] = {{$prevfile, data$fvar, ");
-    print(OUTPUT "data$fvar + ". (length($file) + 1) .", ");
-    print(OUTPUT "sizeof(data$fvar) - ". (length($file) + 1) ."}};\n\n");
-}
-
-print(OUTPUT "#define FS_ROOT file$fvars[$i - 1]\n\n");
-print(OUTPUT "#define FS_NUMFILES $i\n");
+#!/usr/bin/perl
+
+open(OUTPUT, "> fsdata.c");
+
+chdir("fs");
+open(FILES, "find . -type f |");
+
+while($file = <FILES>) {
+
+    # Do not include files in CVS directories nor backup files.
+    if($file =~ /(CVS|~)/) {
+    	next;
+    }
+    
+    chop($file);
+    
+    open(HEADER, "> /tmp/header") || die $!;
+    if($file =~ /404/) {
+	print(HEADER "HTTP/1.0 404 File not found\r\n");
+    } else {
+	print(HEADER "HTTP/1.0 200 OK\r\n");
+    }
+    print(HEADER "Server: lwIP/pre-0.6 (http://www.sics.se/~adam/lwip/)\r\n");
+    if($file =~ /\.html$/) {
+	print(HEADER "Content-type: text/html\r\n");
+    } elsif($file =~ /\.gif$/) {
+	print(HEADER "Content-type: image/gif\r\n");
+    } elsif($file =~ /\.png$/) {
+	print(HEADER "Content-type: image/png\r\n");
+    } elsif($file =~ /\.jpg$/) {
+	print(HEADER "Content-type: image/jpeg\r\n");
+    } elsif($file =~ /\.class$/) {
+	print(HEADER "Content-type: application/octet-stream\r\n");
+    } elsif($file =~ /\.ram$/) {
+	print(HEADER "Content-type: audio/x-pn-realaudio\r\n");    
+    } else {
+	print(HEADER "Content-type: text/plain\r\n");
+    }
+    print(HEADER "\r\n");
+    close(HEADER);
+
+    unless($file =~ /\.plain$/ || $file =~ /cgi/) {
+	system("cat /tmp/header $file > /tmp/file");
+    } else {
+	system("cp $file /tmp/file");
+    }
+    
+    open(FILE, "/tmp/file");
+    unlink("/tmp/file");
+    unlink("/tmp/header");
+
+    $file =~ s/\.//;
+    $fvar = $file;
+    $fvar =~ s-/-_-g;
+    $fvar =~ s-\.-_-g;
+    print(OUTPUT "static const unsigned char data".$fvar."[] = {\n");
+    print(OUTPUT "\t/* $file */\n\t");
+    for($j = 0; $j < length($file); $j++) {
+	printf(OUTPUT "%#02x, ", unpack("C", substr($file, $j, 1)));
+    }
+    printf(OUTPUT "0,\n");
+    
+    
+    $i = 0;
+    while(read(FILE, $data, 1)) {
+        if($i == 0) {
+            print(OUTPUT "\t");
+        }
+        printf(OUTPUT "%#02x, ", unpack("C", $data));
+        $i++;
+        if($i == 10) {
+            print(OUTPUT "\n");
+            $i = 0;
+        }
+    }
+    print(OUTPUT "};\n\n");
+    close(FILE);
+    push(@fvars, $fvar);
+    push(@files, $file);
+}
+
+for($i = 0; $i < @fvars; $i++) {
+    $file = $files[$i];
+    $fvar = $fvars[$i];
+
+    if($i == 0) {
+        $prevfile = "NULL";
+    } else {
+        $prevfile = "file" . $fvars[$i - 1];
+    }
+    print(OUTPUT "const struct fsdata_file file".$fvar."[] = {{$prevfile, data$fvar, ");
+    print(OUTPUT "data$fvar + ". (length($file) + 1) .", ");
+    print(OUTPUT "sizeof(data$fvar) - ". (length($file) + 1) ."}};\n\n");
+}
+
+print(OUTPUT "#define FS_ROOT file$fvars[$i - 1]\n\n");
+print(OUTPUT "#define FS_NUMFILES $i\n");

+ 1033 - 1251
libs/thirdparty/lwip_2.1.2/src/apps/http/makefsdata/makefsdata.c → libs/thirdparty/LwIP/src/apps/httpd/makefsdata/makefsdata.c

@@ -1,1251 +1,1033 @@
-/**
- * makefsdata: Converts a directory structure for use with the lwIP httpd.
- *
- * This file is part of the lwIP TCP/IP stack.
- *
- * Author: Jim Pettinato
- *         Simon Goldschmidt
- *
- * @todo:
- * - take TCP_MSS, LWIP_TCP_TIMESTAMPS and
- *   PAYLOAD_ALIGN_TYPE/PAYLOAD_ALIGNMENT as arguments
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <sys/stat.h>
-
-#include "tinydir.h"
-
-/** Makefsdata can generate *all* files deflate-compressed (where file size shrinks).
- * Since nearly all browsers support this, this is a good way to reduce ROM size.
- * To compress the files, "miniz.c" must be downloaded seperately.
- */
-#ifndef MAKEFS_SUPPORT_DEFLATE
-#define MAKEFS_SUPPORT_DEFLATE 0
-#endif
-
-#define COPY_BUFSIZE (1024*1024) /* 1 MByte */
-
-#if MAKEFS_SUPPORT_DEFLATE
-#include "../miniz.c"
-
-typedef unsigned char uint8;
-typedef unsigned short uint16;
-typedef unsigned int uint;
-
-#define my_max(a,b) (((a) > (b)) ? (a) : (b))
-#define my_min(a,b) (((a) < (b)) ? (a) : (b))
-
-/* COMP_OUT_BUF_SIZE is the size of the output buffer used during compression.
-   COMP_OUT_BUF_SIZE must be >= 1 and <= OUT_BUF_SIZE */
-#define COMP_OUT_BUF_SIZE COPY_BUFSIZE
-
-/* OUT_BUF_SIZE is the size of the output buffer used during decompression.
-   OUT_BUF_SIZE must be a power of 2 >= TINFL_LZ_DICT_SIZE (because the low-level decompressor not only writes, but reads from the output buffer as it decompresses) */
-#define OUT_BUF_SIZE COPY_BUFSIZE
-static uint8 s_outbuf[OUT_BUF_SIZE];
-static uint8 s_checkbuf[OUT_BUF_SIZE];
-
-/* tdefl_compressor contains all the state needed by the low-level compressor so it's a pretty big struct (~300k).
-   This example makes it a global vs. putting it on the stack, of course in real-world usage you'll probably malloc() or new it. */
-tdefl_compressor g_deflator;
-tinfl_decompressor g_inflator;
-
-int deflate_level = 10; /* default compression level, can be changed via command line */
-#define USAGE_ARG_DEFLATE " [-defl<:compr_level>]"
-#else /* MAKEFS_SUPPORT_DEFLATE */
-#define USAGE_ARG_DEFLATE ""
-#endif /* MAKEFS_SUPPORT_DEFLATE */
-
-#ifdef WIN32
-
-#define GETCWD(path, len)             GetCurrentDirectoryA(len, path)
-#define CHDIR(path)                   SetCurrentDirectoryA(path)
-#define CHDIR_SUCCEEDED(ret)          (ret == TRUE)
-
-#elif __linux__
-
-#define GETCWD(path, len)             getcwd(path, len)
-#define CHDIR(path)                   chdir(path)
-#define CHDIR_SUCCEEDED(ret)          (ret == 0)
-
-#else
-
-#error makefsdata not supported on this platform
-
-#endif
-
-#define NEWLINE     "\r\n"
-#define NEWLINE_LEN 2
-
-/* define this to get the header variables we use to build HTTP headers */
-#define LWIP_HTTPD_DYNAMIC_HEADERS 1
-#define LWIP_HTTPD_SSI             1
-#include "lwip/init.h"
-#include "../httpd_structs.h"
-#include "lwip/apps/fs.h"
-
-#include "../core/inet_chksum.c"
-#include "../core/def.c"
-
-/** (Your server name here) */
-const char *serverID = "Server: "HTTPD_SERVER_AGENT"\r\n";
-char serverIDBuffer[1024];
-
-/* change this to suit your MEM_ALIGNMENT */
-#define PAYLOAD_ALIGNMENT 4
-/* set this to 0 to prevent aligning payload */
-#define ALIGN_PAYLOAD 1
-/* define this to a type that has the required alignment */
-#define PAYLOAD_ALIGN_TYPE "unsigned int"
-static int payload_alingment_dummy_counter = 0;
-
-#define HEX_BYTES_PER_LINE 16
-
-#define MAX_PATH_LEN 256
-
-struct file_entry {
-  struct file_entry *next;
-  const char *filename_c;
-};
-
-int process_sub(FILE *data_file, FILE *struct_file);
-int process_file(FILE *data_file, FILE *struct_file, const char *filename);
-int file_write_http_header(FILE *data_file, const char *filename, int file_size, u16_t *http_hdr_len,
-                           u16_t *http_hdr_chksum, u8_t provide_content_len, int is_compressed);
-int file_put_ascii(FILE *file, const char *ascii_string, int len, int *i);
-int s_put_ascii(char *buf, const char *ascii_string, int len, int *i);
-void concat_files(const char *file1, const char *file2, const char *targetfile);
-int check_path(char *path, size_t size);
-static int checkSsiByFilelist(const char* filename_listfile);
-static int ext_in_list(const char* filename, const char *ext_list);
-static int file_to_exclude(const char* filename);
-static int file_can_be_compressed(const char* filename);
-
-/* 5 bytes per char + 3 bytes per line */
-static char file_buffer_c[COPY_BUFSIZE * 5 + ((COPY_BUFSIZE / HEX_BYTES_PER_LINE) * 3)];
-
-char curSubdir[MAX_PATH_LEN];
-char lastFileVar[MAX_PATH_LEN];
-char hdr_buf[4096];
-
-unsigned char processSubs = 1;
-unsigned char includeHttpHeader = 1;
-unsigned char useHttp11 = 0;
-unsigned char supportSsi = 1;
-unsigned char precalcChksum = 0;
-unsigned char includeLastModified = 0;
-#if MAKEFS_SUPPORT_DEFLATE
-unsigned char deflateNonSsiFiles = 0;
-size_t deflatedBytesReduced = 0;
-size_t overallDataBytes = 0;
-#endif
-const char *exclude_list = NULL;
-const char *ncompress_list = NULL;
-
-struct file_entry *first_file = NULL;
-struct file_entry *last_file = NULL;
-
-static char *ssi_file_buffer;
-static char **ssi_file_lines;
-static size_t ssi_file_num_lines;
-
-static void print_usage(void)
-{
-  printf(" Usage: htmlgen [targetdir] [-s] [-e] [-11] [-nossi] [-ssi:<filename>] [-c] [-f:<filename>] [-m] [-svr:<name>] [-x:<ext_list>] [-xc:<ext_list>" USAGE_ARG_DEFLATE NEWLINE NEWLINE);
-  printf("   targetdir: relative or absolute path to files to convert" NEWLINE);
-  printf("   switch -s: toggle processing of subdirectories (default is on)" NEWLINE);
-  printf("   switch -e: exclude HTTP header from file (header is created at runtime, default is off)" NEWLINE);
-  printf("   switch -11: include HTTP 1.1 header (1.0 is default)" NEWLINE);
-  printf("   switch -nossi: no support for SSI (cannot calculate Content-Length for SSI)" NEWLINE);
-  printf("   switch -ssi: ssi filename (ssi support controlled by file list, not by extension)" NEWLINE);
-  printf("   switch -c: precalculate checksums for all pages (default is off)" NEWLINE);
-  printf("   switch -f: target filename (default is \"fsdata.c\")" NEWLINE);
-  printf("   switch -m: include \"Last-Modified\" header based on file time" NEWLINE);
-  printf("   switch -svr: server identifier sent in HTTP response header ('Server' field)" NEWLINE);
-  printf("   switch -x: comma separated list of extensions of files to exclude (e.g., -x:json,txt)" NEWLINE);
-  printf("   switch -xc: comma separated list of extensions of files to not compress (e.g., -xc:mp3,jpg)" NEWLINE);
-#if MAKEFS_SUPPORT_DEFLATE
-  printf("   switch -defl: deflate-compress all non-SSI files (with opt. compr.-level, default=10)" NEWLINE);
-  printf("                 ATTENTION: browser has to support \"Content-Encoding: deflate\"!" NEWLINE);
-#endif
-  printf("   if targetdir not specified, htmlgen will attempt to" NEWLINE);
-  printf("   process files in subdirectory 'fs'" NEWLINE);
-}
-
-int main(int argc, char *argv[])
-{
-  char path[MAX_PATH_LEN];
-  char appPath[MAX_PATH_LEN];
-  FILE *data_file;
-  FILE *struct_file;
-  int filesProcessed;
-  int i;
-  char targetfile[MAX_PATH_LEN];
-  strcpy(targetfile, "fsdata.c");
-
-  memset(path, 0, sizeof(path));
-  memset(appPath, 0, sizeof(appPath));
-
-  printf(NEWLINE " makefsdata - HTML to C source converter" NEWLINE);
-  printf("     by Jim Pettinato               - circa 2003 " NEWLINE);
-  printf("     extended by Simon Goldschmidt  - 2009 " NEWLINE NEWLINE);
-
-  LWIP_ASSERT("sizeof(hdr_buf) must fit into an u16_t", sizeof(hdr_buf) <= 0xffff);
-
-  strcpy(path, "fs");
-  for (i = 1; i < argc; i++) {
-    if (argv[i] == NULL) {
-      continue;
-    }
-    if (argv[i][0] == '-') {
-      if (strstr(argv[i], "-svr:") == argv[i]) {
-        snprintf(serverIDBuffer, sizeof(serverIDBuffer), "Server: %s\r\n", &argv[i][5]);
-        serverID = serverIDBuffer;
-        printf("Using Server-ID: \"%s\"\n", serverID);
-      } else if (!strcmp(argv[i], "-s")) {
-        processSubs = 0;
-      } else if (!strcmp(argv[i], "-e")) {
-        includeHttpHeader = 0;
-      } else if (!strcmp(argv[i], "-11")) {
-        useHttp11 = 1;
-      } else if (!strcmp(argv[i], "-nossi")) {
-        supportSsi = 0;
-      } else if (strstr(argv[i], "-ssi:") == argv[i]) {
-        const char* ssi_list_filename = &argv[i][5];
-        if (checkSsiByFilelist(ssi_list_filename)) {
-          printf("Reading list of SSI files from \"%s\"\n", ssi_list_filename);
-        } else {
-          printf("Failed to load list of SSI files from \"%s\"\n", ssi_list_filename);
-        }
-      } else if (!strcmp(argv[i], "-c")) {
-        precalcChksum = 1;
-      } else if (strstr(argv[i], "-f:") == argv[i]) {
-        strncpy(targetfile, &argv[i][3], sizeof(targetfile) - 1);
-        targetfile[sizeof(targetfile) - 1] = 0;
-        printf("Writing to file \"%s\"\n", targetfile);
-      } else if (!strcmp(argv[i], "-m")) {
-        includeLastModified = 1;
-      } else if (!strcmp(argv[i], "-defl")) {
-#if MAKEFS_SUPPORT_DEFLATE
-        char *colon = strstr(argv[i], ":");
-        if (colon) {
-          if (colon[1] != 0) {
-            int defl_level = atoi(&colon[1]);
-            if ((defl_level >= 0) && (defl_level <= 10)) {
-              deflate_level = defl_level;
-            } else {
-              printf("ERROR: deflate level must be [0..10]" NEWLINE);
-              exit(0);
-            }
-          }
-        }
-        deflateNonSsiFiles = 1;
-        printf("Deflating all non-SSI files with level %d (but only if size is reduced)" NEWLINE, deflate_level);
-#else
-        printf("WARNING: Deflate support is disabled\n");
-#endif
-      } else if (strstr(argv[i], "-x:") == argv[i]) {
-        exclude_list = &argv[i][3];
-        printf("Excluding files with extensions %s" NEWLINE, exclude_list);
-      } else if (strstr(argv[i], "-xc:") == argv[i]) {
-        ncompress_list = &argv[i][4];
-        printf("Skipping compresion for files with extensions %s" NEWLINE, ncompress_list);
-      } else if ((strstr(argv[i], "-?")) || (strstr(argv[i], "-h"))) {
-        print_usage();
-        exit(0);
-      }
-    } else if ((argv[i][0] == '/') && (argv[i][1] == '?') && (argv[i][2] == 0)) {
-      print_usage();
-      exit(0);
-    } else {
-      strncpy(path, argv[i], sizeof(path) - 1);
-      path[sizeof(path) - 1] = 0;
-    }
-  }
-
-  if (!check_path(path, sizeof(path))) {
-    printf("Invalid path: \"%s\"." NEWLINE, path);
-    exit(-1);
-  }
-
-  GETCWD(appPath, MAX_PATH_LEN);
-  /* if command line param or subdir named 'fs' not found spout usage verbiage */
-  if (!CHDIR_SUCCEEDED(CHDIR(path))) {
-    /* if no subdir named 'fs' (or the one which was given) exists, spout usage verbiage */
-    printf(" Failed to open directory \"%s\"." NEWLINE NEWLINE, path);
-    print_usage();
-    exit(-1);
-  }
-  CHDIR(appPath);
-
-  printf("HTTP %sheader will %s statically included." NEWLINE,
-         (includeHttpHeader ? (useHttp11 ? "1.1 " : "1.0 ") : ""),
-         (includeHttpHeader ? "be" : "not be"));
-
-  curSubdir[0] = '\0'; /* start off in web page's root directory - relative paths */
-  printf("  Processing all files in directory %s", path);
-  if (processSubs) {
-    printf(" and subdirectories..." NEWLINE NEWLINE);
-  } else {
-    printf("..." NEWLINE NEWLINE);
-  }
-
-  data_file = fopen("fsdata.tmp", "wb");
-  if (data_file == NULL) {
-    printf("Failed to create file \"fsdata.tmp\"\n");
-    exit(-1);
-  }
-  struct_file = fopen("fshdr.tmp", "wb");
-  if (struct_file == NULL) {
-    printf("Failed to create file \"fshdr.tmp\"\n");
-    fclose(data_file);
-    exit(-1);
-  }
-
-  CHDIR(path);
-
-  fprintf(data_file, "#include \"lwip/apps/fs.h\"" NEWLINE);
-  fprintf(data_file, "#include \"lwip/def.h\"" NEWLINE NEWLINE NEWLINE);
-
-  fprintf(data_file, "#define file_NULL (struct fsdata_file *) NULL" NEWLINE NEWLINE NEWLINE);
-  /* define FS_FILE_FLAGS_HEADER_INCLUDED to 1 if not defined (compatibility with older httpd/fs) */
-  fprintf(data_file, "#ifndef FS_FILE_FLAGS_HEADER_INCLUDED" NEWLINE "#define FS_FILE_FLAGS_HEADER_INCLUDED 1" NEWLINE "#endif" NEWLINE);
-  /* define FS_FILE_FLAGS_HEADER_PERSISTENT to 0 if not defined (compatibility with older httpd/fs: wasn't supported back then) */
-  fprintf(data_file, "#ifndef FS_FILE_FLAGS_HEADER_PERSISTENT" NEWLINE "#define FS_FILE_FLAGS_HEADER_PERSISTENT 0" NEWLINE "#endif" NEWLINE);
-
-  /* define alignment defines */
-#if ALIGN_PAYLOAD
-  fprintf(data_file, "/* FSDATA_FILE_ALIGNMENT: 0=off, 1=by variable, 2=by include */" NEWLINE "#ifndef FSDATA_FILE_ALIGNMENT" NEWLINE "#define FSDATA_FILE_ALIGNMENT 0" NEWLINE "#endif" NEWLINE);
-#endif
-  fprintf(data_file, "#ifndef FSDATA_ALIGN_PRE"  NEWLINE "#define FSDATA_ALIGN_PRE"  NEWLINE "#endif" NEWLINE);
-  fprintf(data_file, "#ifndef FSDATA_ALIGN_POST" NEWLINE "#define FSDATA_ALIGN_POST" NEWLINE "#endif" NEWLINE);
-#if ALIGN_PAYLOAD
-  fprintf(data_file, "#if FSDATA_FILE_ALIGNMENT==2" NEWLINE "#include \"fsdata_alignment.h\"" NEWLINE "#endif" NEWLINE);
-#endif
-
-  sprintf(lastFileVar, "NULL");
-
-  filesProcessed = process_sub(data_file, struct_file);
-
-  /* data_file now contains all of the raw data.. now append linked list of
-   * file header structs to allow embedded app to search for a file name */
-  fprintf(data_file, NEWLINE NEWLINE);
-  fprintf(struct_file, "#define FS_ROOT file_%s" NEWLINE, lastFileVar);
-  fprintf(struct_file, "#define FS_NUMFILES %d" NEWLINE NEWLINE, filesProcessed);
-
-  fclose(data_file);
-  fclose(struct_file);
-
-  CHDIR(appPath);
-  /* append struct_file to data_file */
-  printf(NEWLINE "Creating target file..." NEWLINE NEWLINE);
-  concat_files("fsdata.tmp", "fshdr.tmp", targetfile);
-
-  /* if succeeded, delete the temporary files */
-  if (remove("fsdata.tmp") != 0) {
-    printf("Warning: failed to delete fsdata.tmp\n");
-  }
-  if (remove("fshdr.tmp") != 0) {
-    printf("Warning: failed to delete fshdr.tmp\n");
-  }
-
-  printf(NEWLINE "Processed %d files - done." NEWLINE, filesProcessed);
-#if MAKEFS_SUPPORT_DEFLATE
-  if (deflateNonSsiFiles) {
-    printf("(Deflated total byte reduction: %d bytes -> %d bytes (%.02f%%)" NEWLINE,
-           (int)overallDataBytes, (int)deflatedBytesReduced, (float)((deflatedBytesReduced * 100.0) / overallDataBytes));
-  }
-#endif
-  printf(NEWLINE);
-
-  while (first_file != NULL) {
-    struct file_entry *fe = first_file;
-    first_file = fe->next;
-    free(fe);
-  }
-
-  if (ssi_file_buffer) {
-    free(ssi_file_buffer);
-  }
-  if (ssi_file_lines) {
-    free(ssi_file_lines);
-  }
-
-  return 0;
-}
-
-int check_path(char *path, size_t size)
-{
-  size_t slen;
-  if (path[0] == 0) {
-    /* empty */
-    return 0;
-  }
-  slen = strlen(path);
-  if (slen >= size) {
-    /* not NULL-terminated */
-    return 0;
-  }
-  while ((slen > 0) && ((path[slen] == '\\') || (path[slen] == '/'))) {
-    /* path should not end with trailing backslash */
-    path[slen] = 0;
-    slen--;
-  }
-  if (slen == 0) {
-    return 0;
-  }
-  return 1;
-}
-
-static void copy_file(const char *filename_in, FILE *fout)
-{
-  FILE *fin;
-  size_t len;
-  void *buf;
-  fin = fopen(filename_in, "rb");
-  if (fin == NULL) {
-    printf("Failed to open file \"%s\"\n", filename_in);
-    exit(-1);
-  }
-  buf = malloc(COPY_BUFSIZE);
-  while ((len = fread(buf, 1, COPY_BUFSIZE, fin)) > 0) {
-    fwrite(buf, 1, len, fout);
-  }
-  free(buf);
-  fclose(fin);
-}
-
-void concat_files(const char *file1, const char *file2, const char *targetfile)
-{
-  FILE *fout;
-  fout = fopen(targetfile, "wb");
-  if (fout == NULL) {
-    printf("Failed to open file \"%s\"\n", targetfile);
-    exit(-1);
-  }
-  copy_file(file1, fout);
-  copy_file(file2, fout);
-  fclose(fout);
-}
-
-int process_sub(FILE *data_file, FILE *struct_file)
-{
-  tinydir_dir dir;
-  int filesProcessed = 0;
-
-  if (processSubs) {
-    /* process subs recursively */
-    size_t sublen = strlen(curSubdir);
-    size_t freelen = sizeof(curSubdir) - sublen - 1;
-    int ret;
-    LWIP_ASSERT("sublen < sizeof(curSubdir)", sublen < sizeof(curSubdir));
-
-    ret = tinydir_open_sorted(&dir, TINYDIR_STRING("."));
-
-    if (ret == 0) {
-      unsigned int i;
-      for (i = 0; i < dir.n_files; i++) {
-        tinydir_file file;
-
-        ret = tinydir_readfile_n(&dir, &file, i);
-
-        if (ret == 0) {
-#if (defined _MSC_VER || defined __MINGW32__) && (defined _UNICODE)
-          size_t num_char_converted;
-          char currName[256];
-          wcstombs_s(&num_char_converted, currName, sizeof(currName), file.name, sizeof(currName));
-#else
-          const char *currName = file.name;
-#endif
-
-          if (currName[0] == '.') {
-            continue;
-          }
-          if (!file.is_dir) {
-            continue;
-          }
-          if (freelen > 0) {
-            CHDIR(currName);
-            strncat(curSubdir, "/", freelen);
-            strncat(curSubdir, currName, freelen - 1);
-            curSubdir[sizeof(curSubdir) - 1] = 0;
-            printf("processing subdirectory %s/..." NEWLINE, curSubdir);
-            filesProcessed += process_sub(data_file, struct_file);
-            CHDIR("..");
-            curSubdir[sublen] = 0;
-          } else {
-            printf("WARNING: cannot process sub due to path length restrictions: \"%s/%s\"\n", curSubdir, currName);
-          }
-        }
-      }
-    }
-
-    ret = tinydir_open_sorted(&dir, TINYDIR_STRING("."));
-    if (ret == 0) {
-      unsigned int i;
-      for (i = 0; i < dir.n_files; i++) {
-        tinydir_file file;
-
-        ret = tinydir_readfile_n(&dir, &file, i);
-
-        if (ret == 0) {
-          if (!file.is_dir) {
-#if (defined _MSC_VER || defined __MINGW32__) && (defined _UNICODE)
-            size_t num_char_converted;
-            char curName[256];
-            wcstombs_s(&num_char_converted, curName, sizeof(curName), file.name, sizeof(curName));
-#else
-            const char *curName = file.name;
-#endif
-
-            if (strcmp(curName, "fsdata.tmp") == 0) {
-              continue;
-            }
-            if (strcmp(curName, "fshdr.tmp") == 0) {
-              continue;
-            }
-            if (file_to_exclude(curName)) {
-              printf("skipping %s/%s by exclude list (-x option)..." NEWLINE, curSubdir, curName);
-              continue;
-            }
-
-            printf("processing %s/%s..." NEWLINE, curSubdir, curName);
-
-            if (process_file(data_file, struct_file, curName) < 0) {
-              printf(NEWLINE "Error... aborting" NEWLINE);
-              return -1;
-            }
-            filesProcessed++;
-          }
-        }
-      }
-    }
-  }
-
-  return filesProcessed;
-}
-
-static u8_t *get_file_data(const char *filename, int *file_size, int can_be_compressed, int *is_compressed)
-{
-  FILE *inFile;
-  size_t fsize = 0;
-  u8_t *buf;
-  size_t r;
-  int rs;
-  LWIP_UNUSED_ARG(r); /* for LWIP_NOASSERT */
-  inFile = fopen(filename, "rb");
-  if (inFile == NULL) {
-    printf("Failed to open file \"%s\"\n", filename);
-    exit(-1);
-  }
-  fseek(inFile, 0, SEEK_END);
-  rs = ftell(inFile);
-  if (rs < 0) {
-    printf("ftell failed with %d\n", errno);
-    exit(-1);
-  }
-  fsize = (size_t)rs;
-  fseek(inFile, 0, SEEK_SET);
-  buf = (u8_t *)malloc(fsize);
-  LWIP_ASSERT("buf != NULL", buf != NULL);
-  r = fread(buf, 1, fsize, inFile);
-  LWIP_ASSERT("r == fsize", r == fsize);
-  *file_size = fsize;
-  *is_compressed = 0;
-#if MAKEFS_SUPPORT_DEFLATE
-  overallDataBytes += fsize;
-  if (deflateNonSsiFiles) {
-    if (can_be_compressed) {
-      if (fsize < OUT_BUF_SIZE) {
-        u8_t *ret_buf;
-        tdefl_status status;
-        size_t in_bytes = fsize;
-        size_t out_bytes = OUT_BUF_SIZE;
-        const void *next_in = buf;
-        void *next_out = s_outbuf;
-        /* create tdefl() compatible flags (we have to compose the low-level flags ourselves, or use tdefl_create_comp_flags_from_zip_params() but that means MINIZ_NO_ZLIB_APIS can't be defined). */
-        mz_uint comp_flags = s_tdefl_num_probes[MZ_MIN(10, deflate_level)] | ((deflate_level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0);
-        if (!deflate_level) {
-          comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS;
-        }
-        status = tdefl_init(&g_deflator, NULL, NULL, comp_flags);
-        if (status != TDEFL_STATUS_OKAY) {
-          printf("tdefl_init() failed!\n");
-          exit(-1);
-        }
-        memset(s_outbuf, 0, sizeof(s_outbuf));
-        status = tdefl_compress(&g_deflator, next_in, &in_bytes, next_out, &out_bytes, TDEFL_FINISH);
-        if (status != TDEFL_STATUS_DONE) {
-          printf("deflate failed: %d\n", status);
-          exit(-1);
-        }
-        LWIP_ASSERT("out_bytes <= COPY_BUFSIZE", out_bytes <= OUT_BUF_SIZE);
-        if (out_bytes < fsize) {
-          ret_buf = (u8_t *)malloc(out_bytes);
-          LWIP_ASSERT("ret_buf != NULL", ret_buf != NULL);
-          memcpy(ret_buf, s_outbuf, out_bytes);
-          {
-            /* sanity-check compression be inflating and comparing to the original */
-            tinfl_status dec_status;
-            tinfl_decompressor inflator;
-            size_t dec_in_bytes = out_bytes;
-            size_t dec_out_bytes = OUT_BUF_SIZE;
-            next_out = s_checkbuf;
-
-            tinfl_init(&inflator);
-            memset(s_checkbuf, 0, sizeof(s_checkbuf));
-            dec_status = tinfl_decompress(&inflator, (const mz_uint8 *)ret_buf, &dec_in_bytes, s_checkbuf, (mz_uint8 *)next_out, &dec_out_bytes, 0);
-            LWIP_ASSERT("tinfl_decompress failed", dec_status == TINFL_STATUS_DONE);
-            LWIP_ASSERT("tinfl_decompress size mismatch", fsize == dec_out_bytes);
-            LWIP_ASSERT("decompressed memcmp failed", !memcmp(s_checkbuf, buf, fsize));
-          }
-          /* free original buffer, use compressed data + size */
-          free(buf);
-          buf = ret_buf;
-          *file_size = out_bytes;
-          printf(" - deflate: %d bytes -> %d bytes (%.02f%%)" NEWLINE, (int)fsize, (int)out_bytes, (float)((out_bytes * 100.0) / fsize));
-          deflatedBytesReduced += (size_t)(fsize - out_bytes);
-          *is_compressed = 1;
-        } else {
-          printf(" - uncompressed: (would be %d bytes larger using deflate)" NEWLINE, (int)(out_bytes - fsize));
-        }
-      } else {
-        printf(" - uncompressed: (file is larger than deflate bufer)" NEWLINE);
-      }
-    } else {
-      printf(" - cannot be compressed" NEWLINE);
-    }
-  }
-#else
-  LWIP_UNUSED_ARG(can_be_compressed);
-#endif
-  fclose(inFile);
-  return buf;
-}
-
-static void process_file_data(FILE *data_file, u8_t *file_data, size_t file_size)
-{
-  size_t written, i, src_off = 0;
-  size_t off = 0;
-  LWIP_UNUSED_ARG(written); /* for LWIP_NOASSERT */
-  for (i = 0; i < file_size; i++) {
-    LWIP_ASSERT("file_buffer_c overflow", off < sizeof(file_buffer_c) - 5);
-    sprintf(&file_buffer_c[off], "0x%02x,", file_data[i]);
-    off += 5;
-    if ((++src_off % HEX_BYTES_PER_LINE) == 0) {
-      LWIP_ASSERT("file_buffer_c overflow", off < sizeof(file_buffer_c) - NEWLINE_LEN);
-      memcpy(&file_buffer_c[off], NEWLINE, NEWLINE_LEN);
-      off += NEWLINE_LEN;
-    }
-    if (off + 20 >= sizeof(file_buffer_c)) {
-      written = fwrite(file_buffer_c, 1, off, data_file);
-      LWIP_ASSERT("written == off", written == off);
-      off = 0;
-    }
-  }
-  written = fwrite(file_buffer_c, 1, off, data_file);
-  LWIP_ASSERT("written == off", written == off);
-}
-
-static int write_checksums(FILE *struct_file, const char *varname,
-                           u16_t hdr_len, u16_t hdr_chksum, const u8_t *file_data, size_t file_size)
-{
-  int chunk_size = TCP_MSS;
-  int offset, src_offset;
-  size_t len;
-  int i = 0;
-#if LWIP_TCP_TIMESTAMPS
-  /* when timestamps are used, usable space is 12 bytes less per segment */
-  chunk_size -= 12;
-#endif
-
-  fprintf(struct_file, "#if HTTPD_PRECALCULATED_CHECKSUM" NEWLINE);
-  fprintf(struct_file, "const struct fsdata_chksum chksums_%s[] = {" NEWLINE, varname);
-
-  if (hdr_len > 0) {
-    /* add checksum for HTTP header */
-    fprintf(struct_file, "{%d, 0x%04x, %d}," NEWLINE, 0, hdr_chksum, hdr_len);
-    i++;
-  }
-  src_offset = 0;
-  for (offset = hdr_len; ; offset += len) {
-    unsigned short chksum;
-    const void *data = (const void *)&file_data[src_offset];
-    len = LWIP_MIN(chunk_size, (int)file_size - src_offset);
-    if (len == 0) {
-      break;
-    }
-    chksum = ~inet_chksum(data, (u16_t)len);
-    /* add checksum for data */
-    fprintf(struct_file, "{%d, 0x%04x, %"SZT_F"}," NEWLINE, offset, chksum, len);
-    i++;
-  }
-  fprintf(struct_file, "};" NEWLINE);
-  fprintf(struct_file, "#endif /* HTTPD_PRECALCULATED_CHECKSUM */" NEWLINE);
-  return i;
-}
-
-static int is_valid_char_for_c_var(char x)
-{
-  if (((x >= 'A') && (x <= 'Z')) ||
-      ((x >= 'a') && (x <= 'z')) ||
-      ((x >= '0') && (x <= '9')) ||
-      (x == '_')) {
-    return 1;
-  }
-  return 0;
-}
-
-static void fix_filename_for_c(char *qualifiedName, size_t max_len)
-{
-  struct file_entry *f;
-  size_t len = strlen(qualifiedName);
-  char *new_name = (char *)malloc(len + 2);
-  int filename_ok;
-  int cnt = 0;
-  size_t i;
-  if (len + 3 == max_len) {
-    printf("File name too long: \"%s\"\n", qualifiedName);
-    exit(-1);
-  }
-  strcpy(new_name, qualifiedName);
-  for (i = 0; i < len; i++) {
-    if (!is_valid_char_for_c_var(new_name[i])) {
-      new_name[i] = '_';
-    }
-  }
-  do {
-    filename_ok = 1;
-    for (f = first_file; f != NULL; f = f->next) {
-      if (!strcmp(f->filename_c, new_name)) {
-        filename_ok = 0;
-        cnt++;
-        /* try next unique file name */
-        sprintf(&new_name[len], "%d", cnt);
-        break;
-      }
-    }
-  } while (!filename_ok && (cnt < 999));
-  if (!filename_ok) {
-    printf("Failed to get unique file name: \"%s\"\n", qualifiedName);
-    exit(-1);
-  }
-  strcpy(qualifiedName, new_name);
-  free(new_name);
-}
-
-static void register_filename(const char *qualifiedName)
-{
-  struct file_entry *fe = (struct file_entry *)malloc(sizeof(struct file_entry));
-  fe->filename_c = strdup(qualifiedName);
-  fe->next = NULL;
-  if (first_file == NULL) {
-    first_file = last_file = fe;
-  } else {
-    last_file->next = fe;
-    last_file = fe;
-  }
-}
-
-static int checkSsiByFilelist(const char* filename_listfile)
-{
-  FILE *f = fopen(filename_listfile, "r");
-  if (f != NULL) {
-    char *buf;
-    long rs;
-    size_t fsize, readcount;
-    size_t i, l, num_lines;
-    char **lines;
-    int state;
-
-    fseek(f, 0, SEEK_END);
-    rs = ftell(f);
-    if (rs < 0) {
-      printf("ftell failed with %d\n", errno);
-      fclose(f);
-      return 0;
-    }
-    fsize = (size_t)rs;
-    fseek(f, 0, SEEK_SET);
-    buf = (char*)malloc(fsize);
-    if (!buf) {
-      printf("failed to allocate ssi file buffer\n");
-      fclose(f);
-      return 0;
-    }
-    memset(buf, 0, fsize);
-    readcount = fread(buf, 1, fsize, f);
-    fclose(f);
-    if ((readcount > fsize) || !readcount) {
-      printf("failed to read data from ssi file\n");
-      free(buf);
-      return 0;
-    }
-
-    /* first pass: get the number of lines (and convert newlines to '0') */
-    num_lines = 1;
-    for (i = 0; i < readcount; i++) {
-      if (buf[i] == '\n') {
-        num_lines++;
-        buf[i] = 0;
-      } else if (buf[i] == '\r') {
-        buf[i] = 0;
-      }
-    }
-    /* allocate the line pointer array */
-    lines = (char**)malloc(sizeof(char*) * num_lines);
-    if (!lines) {
-      printf("failed to allocate ssi line buffer\n");
-      free(buf);
-      return 0;
-    }
-    memset(lines, 0, sizeof(char*) * num_lines);
-    l = 0;
-    state = 0;
-    for (i = 0; i < readcount; i++) {
-      if (state) {
-        /* waiting for null */
-        if (buf[i] == 0) {
-          state = 0;
-        }
-      } else {
-        /* waiting for beginning of new string */
-        if (buf[i] != 0) {
-          LWIP_ASSERT("lines array overflow", l < num_lines);
-          lines[l] = &buf[i];
-          state = 1;
-          l++;
-        }
-      }
-    }
-    LWIP_ASSERT("lines array overflow", l < num_lines);
-
-    ssi_file_buffer = buf;
-    ssi_file_lines = lines;
-    ssi_file_num_lines = l;
-  }
-  return 0;
-}
-
-static int is_ssi_file(const char *filename)
-{
-  if (supportSsi) {
-    if (ssi_file_buffer) {
-      /* compare by list */
-      size_t i;
-      int ret = 0;
-      /* build up the relative path to this file */
-      size_t sublen = strlen(curSubdir);
-      size_t freelen = sizeof(curSubdir) - sublen - 1;
-      strncat(curSubdir, "/", freelen);
-      strncat(curSubdir, filename, freelen - 1);
-      curSubdir[sizeof(curSubdir) - 1] = 0;
-      for (i = 0; i < ssi_file_num_lines; i++) {
-        const char *listed_file = ssi_file_lines[i];
-        /* compare without the leading '/' */
-        if (!strcmp(&curSubdir[1], listed_file)) {
-          ret = 1;
-        }
-      }
-      curSubdir[sublen] = 0;
-      return ret;
-    } else {
-      /* check file extension */
-      size_t loop;
-      for (loop = 0; loop < NUM_SHTML_EXTENSIONS; loop++) {
-        if (strstr(filename, g_pcSSIExtensions[loop])) {
-          return 1;
-        }
-      }
-    }
-  }
-  return 0;
-}
-
-static int ext_in_list(const char* filename, const char *ext_list)
-{
-  int found = 0;
-  const char *ext = ext_list;
-  if (ext_list == NULL) {
-    return 0;
-  }
-  while(*ext != '\0') {
-    const char *comma = strchr(ext, ',');
-    size_t ext_size;
-    size_t filename_size = strlen(filename);
-    if (comma == NULL) {
-      comma = strchr(ext, '\0');
-    }
-    ext_size = comma - ext;
-    if ((filename[filename_size - ext_size - 1] == '.') &&
-      !strncmp(&filename[filename_size - ext_size], ext, ext_size)) {
-        found = 1;
-        break;
-    }
-    ext = comma + 1;
-  }
-
-  return found;
-}
-
-static int file_to_exclude(const char *filename)
-{
-    return (exclude_list != NULL) && ext_in_list(filename, exclude_list);
-}
-
-static int file_can_be_compressed(const char *filename)
-{
-    return (ncompress_list == NULL) || !ext_in_list(filename, ncompress_list);
-}
-
-int process_file(FILE *data_file, FILE *struct_file, const char *filename)
-{
-  char varname[MAX_PATH_LEN];
-  int i = 0;
-  char qualifiedName[MAX_PATH_LEN];
-  int file_size;
-  u16_t http_hdr_chksum = 0;
-  u16_t http_hdr_len = 0;
-  int chksum_count = 0;
-  u8_t flags = 0;
-  u8_t has_content_len;
-  u8_t *file_data;
-  int is_ssi;
-  int can_be_compressed;
-  int is_compressed = 0;
-  int flags_printed;
-
-  /* create qualified name (@todo: prepend slash or not?) */
-  sprintf(qualifiedName, "%s/%s", curSubdir, filename);
-  /* create C variable name */
-  strcpy(varname, qualifiedName);
-  /* convert slashes & dots to underscores */
-  fix_filename_for_c(varname, MAX_PATH_LEN);
-  register_filename(varname);
-#if ALIGN_PAYLOAD
-  /* to force even alignment of array, type 1 */
-  fprintf(data_file, "#if FSDATA_FILE_ALIGNMENT==1" NEWLINE);
-  fprintf(data_file, "static const " PAYLOAD_ALIGN_TYPE " dummy_align_%s = %d;" NEWLINE, varname, payload_alingment_dummy_counter++);
-  fprintf(data_file, "#endif" NEWLINE);
-#endif /* ALIGN_PAYLOAD */
-  fprintf(data_file, "static const unsigned char FSDATA_ALIGN_PRE data_%s[] FSDATA_ALIGN_POST = {" NEWLINE, varname);
-  /* encode source file name (used by file system, not returned to browser) */
-  fprintf(data_file, "/* %s (%"SZT_F" chars) */" NEWLINE, qualifiedName, strlen(qualifiedName) + 1);
-  file_put_ascii(data_file, qualifiedName, strlen(qualifiedName) + 1, &i);
-#if ALIGN_PAYLOAD
-  /* pad to even number of bytes to assure payload is on aligned boundary */
-  while (i % PAYLOAD_ALIGNMENT != 0) {
-    fprintf(data_file, "0x%02x,", 0);
-    i++;
-  }
-#endif /* ALIGN_PAYLOAD */
-  fprintf(data_file, NEWLINE);
-
-  is_ssi = is_ssi_file(filename);
-  if (is_ssi) {
-    flags |= FS_FILE_FLAGS_SSI;
-  }
-  has_content_len = !is_ssi;
-  can_be_compressed = includeHttpHeader && !is_ssi && file_can_be_compressed(filename);
-  file_data = get_file_data(filename, &file_size, can_be_compressed, &is_compressed);
-  if (includeHttpHeader) {
-    file_write_http_header(data_file, filename, file_size, &http_hdr_len, &http_hdr_chksum, has_content_len, is_compressed);
-    flags |= FS_FILE_FLAGS_HEADER_INCLUDED;
-    if (has_content_len) {
-      flags |= FS_FILE_FLAGS_HEADER_PERSISTENT;
-      if (useHttp11) {
-        flags |= FS_FILE_FLAGS_HEADER_HTTPVER_1_1;
-      }
-    }
-  }
-  if (precalcChksum) {
-    chksum_count = write_checksums(struct_file, varname, http_hdr_len, http_hdr_chksum, file_data, file_size);
-  }
-
-  /* build declaration of struct fsdata_file in temp file */
-  fprintf(struct_file, "const struct fsdata_file file_%s[] = { {" NEWLINE, varname);
-  fprintf(struct_file, "file_%s," NEWLINE, lastFileVar);
-  fprintf(struct_file, "data_%s," NEWLINE, varname);
-  fprintf(struct_file, "data_%s + %d," NEWLINE, varname, i);
-  fprintf(struct_file, "sizeof(data_%s) - %d," NEWLINE, varname, i);
-
-  flags_printed = 0;
-  if (flags & FS_FILE_FLAGS_HEADER_INCLUDED) {
-    fputs("FS_FILE_FLAGS_HEADER_INCLUDED", struct_file);
-    flags_printed = 1;
-  }
-  if (flags & FS_FILE_FLAGS_HEADER_PERSISTENT) {
-    if (flags_printed) {
-      fputs(" | ", struct_file);
-    }
-    fputs("FS_FILE_FLAGS_HEADER_PERSISTENT", struct_file);
-    flags_printed = 1;
-  }
-  if (flags & FS_FILE_FLAGS_HEADER_HTTPVER_1_1) {
-    if (flags_printed) {
-      fputs(" | ", struct_file);
-    }
-    fputs("FS_FILE_FLAGS_HEADER_HTTPVER_1_1", struct_file);
-    flags_printed = 1;
-  }
-  if (flags & FS_FILE_FLAGS_SSI) {
-    if (flags_printed) {
-      fputs(" | ", struct_file);
-    }
-    fputs("FS_FILE_FLAGS_SSI", struct_file);
-    flags_printed = 1;
-  }
-  if (!flags_printed) {
-    fputs("0", struct_file);
-  }
-  fputs("," NEWLINE, struct_file);
-  if (precalcChksum) {
-    fprintf(struct_file, "#if HTTPD_PRECALCULATED_CHECKSUM" NEWLINE);
-    fprintf(struct_file, "%d, chksums_%s," NEWLINE, chksum_count, varname);
-    fprintf(struct_file, "#endif /* HTTPD_PRECALCULATED_CHECKSUM */" NEWLINE);
-  }
-  fprintf(struct_file, "}};" NEWLINE NEWLINE);
-  strcpy(lastFileVar, varname);
-
-  /* write actual file contents */
-  i = 0;
-  fprintf(data_file, NEWLINE "/* raw file data (%d bytes) */" NEWLINE, file_size);
-  process_file_data(data_file, file_data, file_size);
-  fprintf(data_file, "};" NEWLINE NEWLINE);
-  free(file_data);
-  return 0;
-}
-
-int file_write_http_header(FILE *data_file, const char *filename, int file_size, u16_t *http_hdr_len,
-                           u16_t *http_hdr_chksum, u8_t provide_content_len, int is_compressed)
-{
-  int i = 0;
-  int response_type = HTTP_HDR_OK;
-  const char *file_type;
-  const char *cur_string;
-  size_t cur_len;
-  int written = 0;
-  size_t hdr_len = 0;
-  u16_t acc;
-  const char *file_ext;
-  size_t j;
-  u8_t provide_last_modified = includeLastModified;
-
-  memset(hdr_buf, 0, sizeof(hdr_buf));
-
-  if (useHttp11) {
-    response_type = HTTP_HDR_OK_11;
-  }
-
-  fprintf(data_file, NEWLINE "/* HTTP header */");
-  if (strstr(filename, "404") == filename) {
-    response_type = HTTP_HDR_NOT_FOUND;
-    if (useHttp11) {
-      response_type = HTTP_HDR_NOT_FOUND_11;
-    }
-  } else if (strstr(filename, "400") == filename) {
-    response_type = HTTP_HDR_BAD_REQUEST;
-    if (useHttp11) {
-      response_type = HTTP_HDR_BAD_REQUEST_11;
-    }
-  } else if (strstr(filename, "501") == filename) {
-    response_type = HTTP_HDR_NOT_IMPL;
-    if (useHttp11) {
-      response_type = HTTP_HDR_NOT_IMPL_11;
-    }
-  }
-  cur_string = g_psHTTPHeaderStrings[response_type];
-  cur_len = strlen(cur_string);
-  fprintf(data_file, NEWLINE "/* \"%s\" (%"SZT_F" bytes) */" NEWLINE, cur_string, cur_len);
-  written += file_put_ascii(data_file, cur_string, cur_len, &i);
-  i = 0;
-  if (precalcChksum) {
-    memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
-    hdr_len += cur_len;
-  }
-
-  cur_string = serverID;
-  cur_len = strlen(cur_string);
-  fprintf(data_file, NEWLINE "/* \"%s\" (%"SZT_F" bytes) */" NEWLINE, cur_string, cur_len);
-  written += file_put_ascii(data_file, cur_string, cur_len, &i);
-  i = 0;
-  if (precalcChksum) {
-    memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
-    hdr_len += cur_len;
-  }
-
-  file_ext = filename;
-  if (file_ext != NULL) {
-    while (strstr(file_ext, ".") != NULL) {
-      file_ext = strstr(file_ext, ".");
-      file_ext++;
-    }
-  }
-  if ((file_ext == NULL) || (*file_ext == 0)) {
-    printf("failed to get extension for file \"%s\", using default.\n", filename);
-    file_type = HTTP_HDR_DEFAULT_TYPE;
-  } else {
-    file_type = NULL;
-    for (j = 0; j < NUM_HTTP_HEADERS; j++) {
-      if (!strcmp(file_ext, g_psHTTPHeaders[j].extension)) {
-        file_type = g_psHTTPHeaders[j].content_type;
-        break;
-      }
-    }
-    if (file_type == NULL) {
-      printf("failed to get file type for extension \"%s\", using default.\n", file_ext);
-      file_type = HTTP_HDR_DEFAULT_TYPE;
-    }
-  }
-
-  /* Content-Length is used for persistent connections in HTTP/1.1 but also for
-     download progress in older versions
-     @todo: just use a big-enough buffer and let the HTTPD send spaces? */
-  if (provide_content_len) {
-    char intbuf[MAX_PATH_LEN];
-    int content_len = file_size;
-    memset(intbuf, 0, sizeof(intbuf));
-    cur_string = g_psHTTPHeaderStrings[HTTP_HDR_CONTENT_LENGTH];
-    cur_len = strlen(cur_string);
-    fprintf(data_file, NEWLINE "/* \"%s%d\r\n\" (%"SZT_F"+ bytes) */" NEWLINE, cur_string, content_len, cur_len + 2);
-    written += file_put_ascii(data_file, cur_string, cur_len, &i);
-    if (precalcChksum) {
-      memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
-      hdr_len += cur_len;
-    }
-
-    lwip_itoa(intbuf, sizeof(intbuf), content_len);
-    strcat(intbuf, "\r\n");
-    cur_len = strlen(intbuf);
-    written += file_put_ascii(data_file, intbuf, cur_len, &i);
-    i = 0;
-    if (precalcChksum) {
-      memcpy(&hdr_buf[hdr_len], intbuf, cur_len);
-      hdr_len += cur_len;
-    }
-  }
-  if (provide_last_modified) {
-    char modbuf[256];
-    struct stat stat_data;
-    struct tm *t;
-    memset(modbuf, 0, sizeof(modbuf));
-    memset(&stat_data, 0, sizeof(stat_data));
-    cur_string = modbuf;
-    strcpy(modbuf, "Last-Modified: ");
-    if (stat(filename, &stat_data) != 0) {
-      printf("stat(%s) failed with error %d\n", filename, errno);
-      exit(-1);
-    }
-    t = gmtime(&stat_data.st_mtime);
-    if (t == NULL) {
-      printf("gmtime() failed with error %d\n", errno);
-      exit(-1);
-    }
-    strftime(&modbuf[15], sizeof(modbuf) - 15, "%a, %d %b %Y %H:%M:%S GMT", t);
-    cur_len = strlen(cur_string);
-    fprintf(data_file, NEWLINE "/* \"%s\"\r\n\" (%"SZT_F"+ bytes) */" NEWLINE, cur_string, cur_len + 2);
-    written += file_put_ascii(data_file, cur_string, cur_len, &i);
-    if (precalcChksum) {
-      memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
-      hdr_len += cur_len;
-    }
-
-    modbuf[0] = 0;
-    strcat(modbuf, "\r\n");
-    cur_len = strlen(modbuf);
-    written += file_put_ascii(data_file, modbuf, cur_len, &i);
-    i = 0;
-    if (precalcChksum) {
-      memcpy(&hdr_buf[hdr_len], modbuf, cur_len);
-      hdr_len += cur_len;
-    }
-  }
-
-  /* HTTP/1.1 implements persistent connections */
-  if (useHttp11) {
-    if (provide_content_len) {
-      cur_string = g_psHTTPHeaderStrings[HTTP_HDR_CONN_KEEPALIVE];
-    } else {
-      /* no Content-Length available, so a persistent connection is no possible
-         because the client does not know the data length */
-      cur_string = g_psHTTPHeaderStrings[HTTP_HDR_CONN_CLOSE];
-    }
-    cur_len = strlen(cur_string);
-    fprintf(data_file, NEWLINE "/* \"%s\" (%"SZT_F" bytes) */" NEWLINE, cur_string, cur_len);
-    written += file_put_ascii(data_file, cur_string, cur_len, &i);
-    i = 0;
-    if (precalcChksum) {
-      memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
-      hdr_len += cur_len;
-    }
-  }
-
-#if MAKEFS_SUPPORT_DEFLATE
-  if (is_compressed) {
-    /* tell the client about the deflate encoding */
-    LWIP_ASSERT("error", deflateNonSsiFiles);
-    cur_string = "Content-Encoding: deflate\r\n";
-    cur_len = strlen(cur_string);
-    fprintf(data_file, NEWLINE "/* \"%s\" (%d bytes) */" NEWLINE, cur_string, cur_len);
-    written += file_put_ascii(data_file, cur_string, cur_len, &i);
-    i = 0;
-  }
-#else
-  LWIP_UNUSED_ARG(is_compressed);
-#endif
-
-  /* write content-type, ATTENTION: this includes the double-CRLF! */
-  cur_string = file_type;
-  cur_len = strlen(cur_string);
-  fprintf(data_file, NEWLINE "/* \"%s\" (%"SZT_F" bytes) */" NEWLINE, cur_string, cur_len);
-  written += file_put_ascii(data_file, cur_string, cur_len, &i);
-  i = 0;
-
-  /* ATTENTION: headers are done now (double-CRLF has been written!) */
-
-  if (precalcChksum) {
-    LWIP_ASSERT("hdr_len + cur_len <= sizeof(hdr_buf)", hdr_len + cur_len <= sizeof(hdr_buf));
-    memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
-    hdr_len += cur_len;
-
-    LWIP_ASSERT("strlen(hdr_buf) == hdr_len", strlen(hdr_buf) == hdr_len);
-    acc = ~inet_chksum(hdr_buf, (u16_t)hdr_len);
-    *http_hdr_len = (u16_t)hdr_len;
-    *http_hdr_chksum = acc;
-  }
-
-  return written;
-}
-
-int file_put_ascii(FILE *file, const char *ascii_string, int len, int *i)
-{
-  int x;
-  for (x = 0; x < len; x++) {
-    unsigned char cur = ascii_string[x];
-    fprintf(file, "0x%02x,", cur);
-    if ((++(*i) % HEX_BYTES_PER_LINE) == 0) {
-      fprintf(file, NEWLINE);
-    }
-  }
-  return len;
-}
-
-int s_put_ascii(char *buf, const char *ascii_string, int len, int *i)
-{
-  int x;
-  int idx = 0;
-  for (x = 0; x < len; x++) {
-    unsigned char cur = ascii_string[x];
-    sprintf(&buf[idx], "0x%02x,", cur);
-    idx += 5;
-    if ((++(*i) % HEX_BYTES_PER_LINE) == 0) {
-      sprintf(&buf[idx], NEWLINE);
-      idx += NEWLINE_LEN;
-    }
-  }
-  return len;
-}
+/**
+ * makefsdata: Converts a directory structure for use with the lwIP httpd.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Jim Pettinato
+ *         Simon Goldschmidt
+ *
+ * @todo:
+ * - take TCP_MSS, LWIP_TCP_TIMESTAMPS and
+ *   PAYLOAD_ALIGN_TYPE/PAYLOAD_ALIGNMENT as arguments
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef WIN32
+#define WIN32_LEAN_AND_MEAN
+#include "windows.h"
+#else
+#include <dir.h>
+#endif
+#include <dos.h>
+#include <string.h>
+#include <time.h>
+#include <sys/stat.h>
+
+/** Makefsdata can generate *all* files deflate-compressed (where file size shrinks).
+ * Since nearly all browsers support this, this is a good way to reduce ROM size.
+ * To compress the files, "miniz.c" must be downloaded seperately.
+ */
+#ifndef MAKEFS_SUPPORT_DEFLATE
+#define MAKEFS_SUPPORT_DEFLATE 0
+#endif
+
+#define COPY_BUFSIZE (1024*1024) /* 1 MByte */
+
+#if MAKEFS_SUPPORT_DEFLATE
+#include "../miniz.c"
+
+typedef unsigned char uint8;
+typedef unsigned short uint16;
+typedef unsigned int uint;
+
+#define my_max(a,b) (((a) > (b)) ? (a) : (b))
+#define my_min(a,b) (((a) < (b)) ? (a) : (b))
+
+/* COMP_OUT_BUF_SIZE is the size of the output buffer used during compression.
+   COMP_OUT_BUF_SIZE must be >= 1 and <= OUT_BUF_SIZE */
+#define COMP_OUT_BUF_SIZE COPY_BUFSIZE
+
+/* OUT_BUF_SIZE is the size of the output buffer used during decompression.
+   OUT_BUF_SIZE must be a power of 2 >= TINFL_LZ_DICT_SIZE (because the low-level decompressor not only writes, but reads from the output buffer as it decompresses) */
+#define OUT_BUF_SIZE COPY_BUFSIZE
+static uint8 s_outbuf[OUT_BUF_SIZE];
+static uint8 s_checkbuf[OUT_BUF_SIZE];
+
+/* tdefl_compressor contains all the state needed by the low-level compressor so it's a pretty big struct (~300k).
+   This example makes it a global vs. putting it on the stack, of course in real-world usage you'll probably malloc() or new it. */
+tdefl_compressor g_deflator;
+tinfl_decompressor g_inflator;
+
+int deflate_level = 10; /* default compression level, can be changed via command line */
+#define USAGE_ARG_DEFLATE " [-defl<:compr_level>]"
+#else /* MAKEFS_SUPPORT_DEFLATE */
+#define USAGE_ARG_DEFLATE ""
+#endif /* MAKEFS_SUPPORT_DEFLATE */
+
+/* Compatibility defines Win32 vs. DOS */
+#ifdef WIN32
+
+#define FIND_T                        WIN32_FIND_DATAA
+#define FIND_T_FILENAME(fInfo)        (fInfo.cFileName)
+#define FIND_T_IS_DIR(fInfo)          ((fInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
+#define FIND_T_IS_FILE(fInfo)         ((fInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
+#define FIND_RET_T                    HANDLE
+#define FINDFIRST_FILE(path, result)  FindFirstFileA(path, result)
+#define FINDFIRST_DIR(path, result)   FindFirstFileA(path, result)
+#define FINDNEXT(ff_res, result)      FindNextFileA(ff_res, result)
+#define FINDFIRST_SUCCEEDED(ret)      (ret != INVALID_HANDLE_VALUE)
+#define FINDNEXT_SUCCEEDED(ret)       (ret == TRUE)
+
+#define GETCWD(path, len)             GetCurrentDirectoryA(len, path)
+#define CHDIR(path)                   SetCurrentDirectoryA(path)
+#define CHDIR_SUCCEEDED(ret)          (ret == TRUE)
+
+#else
+
+#define FIND_T                        struct ffblk
+#define FIND_T_FILENAME(fInfo)        (fInfo.ff_name)
+#define FIND_T_IS_DIR(fInfo)          ((fInfo.ff_attrib & FA_DIREC) == FA_DIREC)
+#define FIND_T_IS_FILE(fInfo)         (1)
+#define FIND_RET_T                    int
+#define FINDFIRST_FILE(path, result)  findfirst(path, result, FA_ARCH)
+#define FINDFIRST_DIR(path, result)   findfirst(path, result, FA_DIREC)
+#define FINDNEXT(ff_res, result)      FindNextFileA(ff_res, result)
+#define FINDFIRST_SUCCEEDED(ret)      (ret == 0)
+#define FINDNEXT_SUCCEEDED(ret)       (ret == 0)
+
+#define GETCWD(path, len)             getcwd(path, len)
+#define CHDIR(path)                   chdir(path)
+#define CHDIR_SUCCEEDED(ret)          (ret == 0)
+
+#endif
+
+#define NEWLINE     "\r\n"
+#define NEWLINE_LEN 2
+
+/* define this to get the header variables we use to build HTTP headers */
+#define LWIP_HTTPD_DYNAMIC_HEADERS 1
+#define LWIP_HTTPD_SSI             1
+#include "lwip/init.h"
+#include "../httpd_structs.h"
+#include "lwip/apps/fs.h"
+
+#include "../core/inet_chksum.c"
+#include "../core/def.c"
+
+/** (Your server name here) */
+const char *serverID = "Server: "HTTPD_SERVER_AGENT"\r\n";
+char serverIDBuffer[1024];
+
+/* change this to suit your MEM_ALIGNMENT */
+#define PAYLOAD_ALIGNMENT 4
+/* set this to 0 to prevent aligning payload */
+#define ALIGN_PAYLOAD 1
+/* define this to a type that has the required alignment */
+#define PAYLOAD_ALIGN_TYPE "unsigned int"
+static int payload_alingment_dummy_counter = 0;
+
+#define HEX_BYTES_PER_LINE 16
+
+#define MAX_PATH_LEN 256
+
+struct file_entry
+{
+   struct file_entry* next;
+   const char* filename_c;
+};
+
+int process_sub(FILE *data_file, FILE *struct_file);
+int process_file(FILE *data_file, FILE *struct_file, const char *filename);
+int file_write_http_header(FILE *data_file, const char *filename, int file_size, u16_t *http_hdr_len,
+                           u16_t *http_hdr_chksum, u8_t provide_content_len, int is_compressed);
+int file_put_ascii(FILE *file, const char *ascii_string, int len, int *i);
+int s_put_ascii(char *buf, const char *ascii_string, int len, int *i);
+void concat_files(const char *file1, const char *file2, const char *targetfile);
+int check_path(char* path, size_t size);
+
+/* 5 bytes per char + 3 bytes per line */
+static char file_buffer_c[COPY_BUFSIZE * 5 + ((COPY_BUFSIZE / HEX_BYTES_PER_LINE) * 3)];
+
+char curSubdir[MAX_PATH_LEN];
+char lastFileVar[MAX_PATH_LEN];
+char hdr_buf[4096];
+
+unsigned char processSubs = 1;
+unsigned char includeHttpHeader = 1;
+unsigned char useHttp11 = 0;
+unsigned char supportSsi = 1;
+unsigned char precalcChksum = 0;
+unsigned char includeLastModified = 0;
+#if MAKEFS_SUPPORT_DEFLATE
+unsigned char deflateNonSsiFiles = 0;
+size_t deflatedBytesReduced = 0;
+size_t overallDataBytes = 0;
+#endif
+
+struct file_entry* first_file = NULL;
+struct file_entry* last_file = NULL;
+
+static void print_usage(void)
+{
+  printf(" Usage: htmlgen [targetdir] [-s] [-e] [-i] [-11] [-nossi] [-c] [-f:<filename>] [-m] [-svr:<name>]" USAGE_ARG_DEFLATE NEWLINE NEWLINE);
+  printf("   targetdir: relative or absolute path to files to convert" NEWLINE);
+  printf("   switch -s: toggle processing of subdirectories (default is on)" NEWLINE);
+  printf("   switch -e: exclude HTTP header from file (header is created at runtime, default is off)" NEWLINE);
+  printf("   switch -11: include HTTP 1.1 header (1.0 is default)" NEWLINE);
+  printf("   switch -nossi: no support for SSI (cannot calculate Content-Length for SSI)" NEWLINE);
+  printf("   switch -c: precalculate checksums for all pages (default is off)" NEWLINE);
+  printf("   switch -f: target filename (default is \"fsdata.c\")" NEWLINE);
+  printf("   switch -m: include \"Last-Modified\" header based on file time" NEWLINE);
+  printf("   switch -svr: server identifier sent in HTTP response header ('Server' field)" NEWLINE);
+#if MAKEFS_SUPPORT_DEFLATE
+  printf("   switch -defl: deflate-compress all non-SSI files (with opt. compr.-level, default=10)" NEWLINE);
+  printf("                 ATTENTION: browser has to support \"Content-Encoding: deflate\"!" NEWLINE);
+#endif
+  printf("   if targetdir not specified, htmlgen will attempt to" NEWLINE);
+  printf("   process files in subdirectory 'fs'" NEWLINE);
+}
+
+int main(int argc, char *argv[])
+{
+  char path[MAX_PATH_LEN];
+  char appPath[MAX_PATH_LEN];
+  FILE *data_file;
+  FILE *struct_file;
+  int filesProcessed;
+  int i;
+  char targetfile[MAX_PATH_LEN];
+  strcpy(targetfile, "fsdata.c");
+
+  memset(path, 0, sizeof(path));
+  memset(appPath, 0, sizeof(appPath));
+
+  printf(NEWLINE " makefsdata - HTML to C source converter" NEWLINE);
+  printf("     by Jim Pettinato               - circa 2003 " NEWLINE);
+  printf("     extended by Simon Goldschmidt  - 2009 " NEWLINE NEWLINE);
+
+  strcpy(path, "fs");
+  for (i = 1; i < argc; i++) {
+    if (argv[i] == NULL) {
+      continue;
+    }
+    if (argv[i][0] == '-') {
+      if (strstr(argv[i], "-svr:") == argv[i]) {
+        snprintf(serverIDBuffer, sizeof(serverIDBuffer), "Server: %s\r\n", &argv[i][5]);
+        serverID = serverIDBuffer;
+        printf("Using Server-ID: \"%s\"\n", serverID);
+      } else if (strstr(argv[i], "-s") == argv[i]) {
+        processSubs = 0;
+      } else if (strstr(argv[i], "-e") == argv[i]) {
+        includeHttpHeader = 0;
+      } else if (strstr(argv[i], "-11") == argv[i]) {
+        useHttp11 = 1;
+      } else if (strstr(argv[i], "-nossi") == argv[i]) {
+        supportSsi = 0;
+      } else if (strstr(argv[i], "-c") == argv[i]) {
+        precalcChksum = 1;
+      } else if (strstr(argv[i], "-f:") == argv[i]) {
+        strncpy(targetfile, &argv[i][3], sizeof(targetfile) - 1);
+        targetfile[sizeof(targetfile) - 1] = 0;
+        printf("Writing to file \"%s\"\n", targetfile);
+      } else if (strstr(argv[i], "-m") == argv[i]) {
+        includeLastModified = 1;
+      } else if (strstr(argv[i], "-defl") == argv[i]) {
+#if MAKEFS_SUPPORT_DEFLATE
+        char* colon = strstr(argv[i], ":");
+        if (colon) {
+          if (colon[1] != 0) {
+            int defl_level = atoi(&colon[1]);
+            if ((defl_level >= 0) && (defl_level <= 10)) {
+              deflate_level = defl_level;
+            } else {
+              printf("ERROR: deflate level must be [0..10]" NEWLINE);
+              exit(0);
+            }
+          }
+        }
+        deflateNonSsiFiles = 1;
+        printf("Deflating all non-SSI files with level %d (but only if size is reduced)" NEWLINE, deflate_level);
+#else
+        printf("WARNING: Deflate support is disabled\n");
+#endif
+      } else if ((strstr(argv[i], "-?")) || (strstr(argv[i], "-h"))) {
+        print_usage();
+        exit(0);
+      }
+    } else if ((argv[i][0] == '/') && (argv[i][1] == '?') && (argv[i][2] == 0)) {
+      print_usage();
+      exit(0);
+    } else {
+      strncpy(path, argv[i], sizeof(path)-1);
+      path[sizeof(path)-1] = 0;
+    }
+  }
+
+  if (!check_path(path, sizeof(path))) {
+    printf("Invalid path: \"%s\"." NEWLINE, path);
+    exit(-1);
+  }
+
+  GETCWD(appPath, MAX_PATH_LEN);
+  /* if command line param or subdir named 'fs' not found spout usage verbiage */
+  if (!CHDIR_SUCCEEDED(CHDIR(path))) {
+    /* if no subdir named 'fs' (or the one which was given) exists, spout usage verbiage */
+    printf(" Failed to open directory \"%s\"." NEWLINE NEWLINE, path);
+    print_usage();
+    exit(-1);
+  }
+  CHDIR(appPath);
+
+  printf("HTTP %sheader will %s statically included." NEWLINE,
+    (includeHttpHeader ? (useHttp11 ? "1.1 " : "1.0 ") : ""),
+    (includeHttpHeader ? "be" : "not be"));
+
+  sprintf(curSubdir, "");  /* start off in web page's root directory - relative paths */
+  printf("  Processing all files in directory %s", path);
+  if (processSubs) {
+    printf(" and subdirectories..." NEWLINE NEWLINE);
+  } else {
+    printf("..." NEWLINE NEWLINE);
+  }
+
+  data_file = fopen("fsdata.tmp", "wb");
+  if (data_file == NULL) {
+    printf("Failed to create file \"fsdata.tmp\"\n");
+    exit(-1);
+  }
+  struct_file = fopen("fshdr.tmp", "wb");
+  if (struct_file == NULL) {
+    printf("Failed to create file \"fshdr.tmp\"\n");
+    fclose(data_file);
+    exit(-1);
+  }
+
+  CHDIR(path);
+
+  fprintf(data_file, "#include \"lwip/apps/fs.h\"" NEWLINE);
+  fprintf(data_file, "#include \"lwip/def.h\"" NEWLINE);
+  fprintf(data_file, "#include \"fsdata.h\"" NEWLINE NEWLINE NEWLINE);
+
+  fprintf(data_file, "#define file_NULL (struct fsdata_file *) NULL" NEWLINE NEWLINE NEWLINE);
+  /* define FS_FILE_FLAGS_HEADER_INCLUDED to 1 if not defined (compatibility with older httpd/fs) */
+  fprintf(data_file, "#ifndef FS_FILE_FLAGS_HEADER_INCLUDED" NEWLINE "#define FS_FILE_FLAGS_HEADER_INCLUDED 1" NEWLINE "#endif" NEWLINE);
+  /* define FS_FILE_FLAGS_HEADER_PERSISTENT to 0 if not defined (compatibility with older httpd/fs: wasn't supported back then) */
+  fprintf(data_file, "#ifndef FS_FILE_FLAGS_HEADER_PERSISTENT" NEWLINE "#define FS_FILE_FLAGS_HEADER_PERSISTENT 0" NEWLINE "#endif" NEWLINE);
+
+  /* define alignment defines */
+#if ALIGN_PAYLOAD
+  fprintf(data_file, "/* FSDATA_FILE_ALIGNMENT: 0=off, 1=by variable, 2=by include */" NEWLINE "#ifndef FSDATA_FILE_ALIGNMENT" NEWLINE "#define FSDATA_FILE_ALIGNMENT 0" NEWLINE "#endif" NEWLINE);
+#endif
+  fprintf(data_file, "#ifndef FSDATA_ALIGN_PRE"  NEWLINE "#define FSDATA_ALIGN_PRE"  NEWLINE "#endif" NEWLINE);
+  fprintf(data_file, "#ifndef FSDATA_ALIGN_POST" NEWLINE "#define FSDATA_ALIGN_POST" NEWLINE "#endif" NEWLINE);
+#if ALIGN_PAYLOAD
+  fprintf(data_file, "#if FSDATA_FILE_ALIGNMENT==2" NEWLINE "#include \"fsdata_alignment.h\"" NEWLINE "#endif" NEWLINE);
+#endif
+
+  sprintf(lastFileVar, "NULL");
+
+  filesProcessed = process_sub(data_file, struct_file);
+
+  /* data_file now contains all of the raw data.. now append linked list of
+   * file header structs to allow embedded app to search for a file name */
+  fprintf(data_file, NEWLINE NEWLINE);
+  fprintf(struct_file, "#define FS_ROOT file_%s" NEWLINE, lastFileVar);
+  fprintf(struct_file, "#define FS_NUMFILES %d" NEWLINE NEWLINE, filesProcessed);
+
+  fclose(data_file);
+  fclose(struct_file);
+
+  CHDIR(appPath);
+  /* append struct_file to data_file */
+  printf(NEWLINE "Creating target file..." NEWLINE NEWLINE);
+  concat_files("fsdata.tmp", "fshdr.tmp", targetfile);
+
+  /* if succeeded, delete the temporary files */
+  if (remove("fsdata.tmp") != 0) {
+    printf("Warning: failed to delete fsdata.tmp\n");
+  }
+  if (remove("fshdr.tmp") != 0) {
+    printf("Warning: failed to delete fshdr.tmp\n");
+  }
+
+  printf(NEWLINE "Processed %d files - done." NEWLINE, filesProcessed);
+#if MAKEFS_SUPPORT_DEFLATE
+  if (deflateNonSsiFiles) {
+    printf("(Deflated total byte reduction: %d bytes -> %d bytes (%.02f%%)" NEWLINE,
+      (int)overallDataBytes, (int)deflatedBytesReduced, (float)((deflatedBytesReduced*100.0)/overallDataBytes));
+  }
+#endif
+  printf(NEWLINE);
+
+  while (first_file != NULL) {
+     struct file_entry* fe = first_file;
+     first_file = fe->next;
+     free(fe);
+  }
+
+  return 0;
+}
+
+int check_path(char* path, size_t size)
+{
+  size_t slen;
+  if (path[0] == 0) {
+    /* empty */
+    return 0;
+  }
+  slen = strlen(path);
+  if (slen >= size) {
+    /* not NULL-terminated */
+    return 0;
+  }
+  while ((slen > 0) && ((path[slen] == '\\') || (path[slen] == '/'))) {
+    /* path should not end with trailing backslash */
+    path[slen] = 0;
+    slen--;
+  }
+  if (slen == 0) {
+    return 0;
+  }
+  return 1;
+}
+
+static void copy_file(const char *filename_in, FILE *fout)
+{
+  FILE *fin;
+  size_t len;
+  void* buf;
+  fin = fopen(filename_in, "rb");
+  if (fin == NULL) {
+    printf("Failed to open file \"%s\"\n", filename_in);
+    exit(-1);
+  }
+  buf = malloc(COPY_BUFSIZE);
+  while ((len = fread(buf, 1, COPY_BUFSIZE, fin)) > 0) {
+    fwrite(buf, 1, len, fout);
+  }
+  free(buf);
+  fclose(fin);
+}
+
+void concat_files(const char *file1, const char *file2, const char *targetfile)
+{
+  FILE *fout;
+  fout = fopen(targetfile, "wb");
+  if (fout == NULL) {
+    printf("Failed to open file \"%s\"\n", targetfile);
+    exit(-1);
+  }
+  copy_file(file1, fout);
+  copy_file(file2, fout);
+  fclose(fout);
+}
+
+int process_sub(FILE *data_file, FILE *struct_file)
+{
+  FIND_T fInfo;
+  FIND_RET_T fret;
+  int filesProcessed = 0;
+
+  if (processSubs) {
+    /* process subs recursively */
+    size_t sublen = strlen(curSubdir);
+    size_t freelen = sizeof(curSubdir) - sublen - 1;
+    LWIP_ASSERT("sublen < sizeof(curSubdir)", sublen < sizeof(curSubdir));
+    fret = FINDFIRST_DIR("*", &fInfo);
+    if (FINDFIRST_SUCCEEDED(fret)) {
+      do {
+        const char *curName = FIND_T_FILENAME(fInfo);
+        if ((curName[0] == '.') || (strcmp(curName, "CVS") == 0)) {
+          continue;
+        }
+        if (!FIND_T_IS_DIR(fInfo)) {
+          continue;
+        }
+        if (freelen > 0) {
+           CHDIR(curName);
+           strncat(curSubdir, "/", freelen);
+           strncat(curSubdir, curName, freelen - 1);
+           curSubdir[sizeof(curSubdir) - 1] = 0;
+           printf("processing subdirectory %s/..." NEWLINE, curSubdir);
+           filesProcessed += process_sub(data_file, struct_file);
+           CHDIR("..");
+           curSubdir[sublen] = 0;
+        } else {
+           printf("WARNING: cannot process sub due to path length restrictions: \"%s/%s\"\n", curSubdir, curName);
+        }
+      } while (FINDNEXT_SUCCEEDED(FINDNEXT(fret, &fInfo)));
+    }
+  }
+
+  fret = FINDFIRST_FILE("*.*", &fInfo);
+  if (FINDFIRST_SUCCEEDED(fret)) {
+    /* at least one file in directory */
+    do {
+      if (FIND_T_IS_FILE(fInfo)) {
+        const char *curName = FIND_T_FILENAME(fInfo);
+        printf("processing %s/%s..." NEWLINE, curSubdir, curName);
+        if (process_file(data_file, struct_file, curName) < 0) {
+          printf(NEWLINE "Error... aborting" NEWLINE);
+          return -1;
+        }
+        filesProcessed++;
+      }
+    } while (FINDNEXT_SUCCEEDED(FINDNEXT(fret, &fInfo)));
+  }
+  return filesProcessed;
+}
+
+u8_t* get_file_data(const char* filename, int* file_size, int can_be_compressed, int* is_compressed)
+{
+  FILE *inFile;
+  size_t fsize = 0;
+  u8_t* buf;
+  size_t r;
+  int rs;
+  inFile = fopen(filename, "rb");
+  if (inFile == NULL) {
+    printf("Failed to open file \"%s\"\n", filename);
+    exit(-1);
+  }
+  fseek(inFile, 0, SEEK_END);
+  rs = ftell(inFile);
+  if (rs < 0) {
+     printf("ftell failed with %d\n", errno);
+     exit(-1);
+  }
+  fsize = (size_t)rs;
+  fseek(inFile, 0, SEEK_SET);
+  buf = (u8_t*)malloc(fsize);
+  LWIP_ASSERT("buf != NULL", buf != NULL);
+  r = fread(buf, 1, fsize, inFile);
+  *file_size = fsize;
+  *is_compressed = 0;
+#if MAKEFS_SUPPORT_DEFLATE
+  overallDataBytes += fsize;
+  if (deflateNonSsiFiles) {
+    if (can_be_compressed) {
+      if (fsize < OUT_BUF_SIZE) {
+        u8_t* ret_buf;
+        tdefl_status status;
+        size_t in_bytes = fsize;
+        size_t out_bytes = OUT_BUF_SIZE;
+        const void *next_in = buf;
+        void *next_out = s_outbuf;
+        /* create tdefl() compatible flags (we have to compose the low-level flags ourselves, or use tdefl_create_comp_flags_from_zip_params() but that means MINIZ_NO_ZLIB_APIS can't be defined). */
+        mz_uint comp_flags = s_tdefl_num_probes[MZ_MIN(10, deflate_level)] | ((deflate_level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0);
+        if (!deflate_level) {
+          comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS;
+        }
+        status = tdefl_init(&g_deflator, NULL, NULL, comp_flags);
+        if (status != TDEFL_STATUS_OKAY) {
+          printf("tdefl_init() failed!\n");
+          exit(-1);
+        }
+        memset(s_outbuf, 0, sizeof(s_outbuf));
+        status = tdefl_compress(&g_deflator, next_in, &in_bytes, next_out, &out_bytes, TDEFL_FINISH);
+        if (status != TDEFL_STATUS_DONE) {
+          printf("deflate failed: %d\n", status);
+          exit(-1);
+        }
+        LWIP_ASSERT("out_bytes <= COPY_BUFSIZE", out_bytes <= OUT_BUF_SIZE);
+        if (out_bytes < fsize) {
+          ret_buf = (u8_t*)malloc(out_bytes);
+          LWIP_ASSERT("ret_buf != NULL", ret_buf != NULL);
+          memcpy(ret_buf, s_outbuf, out_bytes);
+          {
+            /* sanity-check compression be inflating and comparing to the original */
+            tinfl_status dec_status;
+            tinfl_decompressor inflator;
+            size_t dec_in_bytes = out_bytes;
+            size_t dec_out_bytes = OUT_BUF_SIZE;
+            next_out = s_checkbuf;
+
+            tinfl_init(&inflator);
+            memset(s_checkbuf, 0, sizeof(s_checkbuf));
+            dec_status = tinfl_decompress(&inflator, (const mz_uint8 *)ret_buf, &dec_in_bytes, s_checkbuf, (mz_uint8 *)next_out, &dec_out_bytes, 0);
+            LWIP_ASSERT("tinfl_decompress failed", dec_status == TINFL_STATUS_DONE);
+            LWIP_ASSERT("tinfl_decompress size mismatch", fsize == dec_out_bytes);
+            LWIP_ASSERT("decompressed memcmp failed", !memcmp(s_checkbuf, buf, fsize));
+          }
+          /* free original buffer, use compressed data + size */
+          free(buf);
+          buf = ret_buf;
+          *file_size = out_bytes;
+          printf(" - deflate: %d bytes -> %d bytes (%.02f%%)" NEWLINE, (int)fsize, (int)out_bytes, (float)((out_bytes*100.0)/fsize));
+          deflatedBytesReduced += (size_t)(fsize - out_bytes);
+          *is_compressed = 1;
+        } else {
+          printf(" - uncompressed: (would be %d bytes larger using deflate)" NEWLINE, (int)(out_bytes - fsize));
+        }
+      } else {
+        printf(" - uncompressed: (file is larger than deflate bufer)" NEWLINE);
+      }
+    } else {
+      printf(" - SSI file, cannot be compressed" NEWLINE);
+    }
+  }
+#else
+  LWIP_UNUSED_ARG(can_be_compressed);
+#endif
+  fclose(inFile);
+  return buf;
+}
+
+void process_file_data(FILE* data_file, u8_t* file_data, size_t file_size)
+{
+  size_t written, i, src_off=0;
+
+  size_t off = 0;
+  for (i = 0; i < file_size; i++) {
+    LWIP_ASSERT("file_buffer_c overflow", off < sizeof(file_buffer_c) - 5);
+    sprintf(&file_buffer_c[off], "0x%02.2x,", file_data[i]);
+    off += 5;
+    if ((++src_off % HEX_BYTES_PER_LINE) == 0) {
+      LWIP_ASSERT("file_buffer_c overflow", off < sizeof(file_buffer_c) - NEWLINE_LEN);
+      memcpy(&file_buffer_c[off], NEWLINE, NEWLINE_LEN);
+      off += NEWLINE_LEN;
+    }
+    if (off + 20 >= sizeof(file_buffer_c)) {
+      written = fwrite(file_buffer_c, 1, off, data_file);
+      LWIP_ASSERT("written == off", written == off);
+      off = 0;
+    }
+  }
+  written = fwrite(file_buffer_c, 1, off, data_file);
+  LWIP_ASSERT("written == off", written == off);
+}
+
+int write_checksums(FILE *struct_file, const char *varname,
+                    u16_t hdr_len, u16_t hdr_chksum, const u8_t* file_data, size_t file_size)
+{
+  int chunk_size = TCP_MSS;
+  int offset, src_offset;
+  size_t len;
+  int i = 0;
+#if LWIP_TCP_TIMESTAMPS
+  /* when timestamps are used, usable space is 12 bytes less per segment */
+  chunk_size -= 12;
+#endif
+
+  fprintf(struct_file, "#if HTTPD_PRECALCULATED_CHECKSUM" NEWLINE);
+  fprintf(struct_file, "const struct fsdata_chksum chksums_%s[] = {" NEWLINE, varname);
+
+  if (hdr_len > 0) {
+    /* add checksum for HTTP header */
+    fprintf(struct_file, "{%d, 0x%04x, %d}," NEWLINE, 0, hdr_chksum, hdr_len);
+    i++;
+  }
+  src_offset = 0;
+  for (offset = hdr_len; ; offset += len) {
+    unsigned short chksum;
+    void* data = (void*)&file_data[src_offset];
+    len = LWIP_MIN(chunk_size, (int)file_size - src_offset);
+    if (len == 0) {
+      break;
+    }
+    chksum = ~inet_chksum(data, (u16_t)len);
+    /* add checksum for data */
+    fprintf(struct_file, "{%d, 0x%04x, %d}," NEWLINE, offset, chksum, len);
+    i++;
+  }
+  fprintf(struct_file, "};" NEWLINE);
+  fprintf(struct_file, "#endif /* HTTPD_PRECALCULATED_CHECKSUM */" NEWLINE);
+  return i;
+}
+
+static int is_valid_char_for_c_var(char x)
+{
+   if (((x >= 'A') && (x <= 'Z')) ||
+       ((x >= 'a') && (x <= 'z')) ||
+       ((x >= '0') && (x <= '9')) ||
+        (x == '_')) {
+      return 1;
+   }
+   return 0;
+}
+
+static void fix_filename_for_c(char* qualifiedName, size_t max_len)
+{
+   struct file_entry* f;
+   size_t len = strlen(qualifiedName);
+   char *new_name = (char*)malloc(len + 2);
+   int filename_ok;
+   int cnt = 0;
+   size_t i;
+   if (len + 3 == max_len) {
+      printf("File name too long: \"%s\"\n", qualifiedName);
+      exit(-1);
+   }
+   strcpy(new_name, qualifiedName);
+   for (i = 0; i < len; i++) {
+      if (!is_valid_char_for_c_var(new_name[i])) {
+         new_name[i] = '_';
+      }
+   }
+   do {
+      filename_ok = 1;
+      for (f = first_file; f != NULL; f = f->next) {
+         if (!strcmp(f->filename_c, new_name)) {
+            filename_ok = 0;
+            cnt++;
+            /* try next unique file name */
+            sprintf(&new_name[len], "%d", cnt);
+            break;
+         }
+      }
+   } while (!filename_ok && (cnt < 999));
+   if (!filename_ok) {
+      printf("Failed to get unique file name: \"%s\"\n", qualifiedName);
+      exit(-1);
+   }
+   strcpy(qualifiedName, new_name);
+   free(new_name);
+}
+
+static void register_filename(const char* qualifiedName)
+{
+   struct file_entry* fe = (struct file_entry*)malloc(sizeof(struct file_entry));
+   fe->filename_c = strdup(qualifiedName);
+   fe->next = NULL;
+   if (first_file == NULL) {
+      first_file = last_file = fe;
+   } else {
+      last_file->next = fe;
+      last_file = fe;
+   }
+}
+
+int is_ssi_file(const char* filename)
+{
+  size_t loop;
+  for (loop = 0; loop < NUM_SHTML_EXTENSIONS; loop++) {
+    if (strstr(filename, g_pcSSIExtensions[loop])) {
+      return 1;
+    }
+  }
+  return 0;
+}
+
+int process_file(FILE *data_file, FILE *struct_file, const char *filename)
+{
+  char varname[MAX_PATH_LEN];
+  int i = 0;
+  char qualifiedName[MAX_PATH_LEN];
+  int file_size;
+  u16_t http_hdr_chksum = 0;
+  u16_t http_hdr_len = 0;
+  int chksum_count = 0;
+  u8_t flags = 0;
+  const char* flags_str;
+  u8_t has_content_len;
+  u8_t* file_data;
+  int is_compressed = 0;
+
+  /* create qualified name (@todo: prepend slash or not?) */
+  sprintf(qualifiedName,"%s/%s", curSubdir, filename);
+  /* create C variable name */
+  strcpy(varname, qualifiedName);
+  /* convert slashes & dots to underscores */
+  fix_filename_for_c(varname, MAX_PATH_LEN);
+  register_filename(varname);
+#if ALIGN_PAYLOAD
+  /* to force even alignment of array, type 1 */
+  fprintf(data_file, "#if FSDATA_FILE_ALIGNMENT==1" NEWLINE);
+  fprintf(data_file, "static const " PAYLOAD_ALIGN_TYPE " dummy_align_%s = %d;" NEWLINE, varname, payload_alingment_dummy_counter++);
+  fprintf(data_file, "#endif" NEWLINE);
+#endif /* ALIGN_PAYLOAD */
+  fprintf(data_file, "static const unsigned char FSDATA_ALIGN_PRE data_%s[] FSDATA_ALIGN_POST = {" NEWLINE, varname);
+  /* encode source file name (used by file system, not returned to browser) */
+  fprintf(data_file, "/* %s (%d chars) */" NEWLINE, qualifiedName, strlen(qualifiedName)+1);
+  file_put_ascii(data_file, qualifiedName, strlen(qualifiedName)+1, &i);
+#if ALIGN_PAYLOAD
+  /* pad to even number of bytes to assure payload is on aligned boundary */
+  while(i % PAYLOAD_ALIGNMENT != 0) {
+    fprintf(data_file, "0x%02.2x,", 0);
+    i++;
+  }
+#endif /* ALIGN_PAYLOAD */
+  fprintf(data_file, NEWLINE);
+
+  has_content_len = !is_ssi_file(filename);
+  file_data = get_file_data(filename, &file_size, includeHttpHeader && has_content_len, &is_compressed);
+  if (includeHttpHeader) {
+    file_write_http_header(data_file, filename, file_size, &http_hdr_len, &http_hdr_chksum, has_content_len, is_compressed);
+    flags = FS_FILE_FLAGS_HEADER_INCLUDED;
+    if (has_content_len) {
+      flags |= FS_FILE_FLAGS_HEADER_PERSISTENT;
+    }
+  }
+  if (precalcChksum) {
+    chksum_count = write_checksums(struct_file, varname, http_hdr_len, http_hdr_chksum, file_data, file_size);
+  }
+
+  /* build declaration of struct fsdata_file in temp file */
+  fprintf(struct_file, "const struct fsdata_file file_%s[] = { {" NEWLINE, varname);
+  fprintf(struct_file, "file_%s," NEWLINE, lastFileVar);
+  fprintf(struct_file, "data_%s," NEWLINE, varname);
+  fprintf(struct_file, "data_%s + %d," NEWLINE, varname, i);
+  fprintf(struct_file, "sizeof(data_%s) - %d," NEWLINE, varname, i);
+  switch(flags)
+  {
+  case(FS_FILE_FLAGS_HEADER_INCLUDED):
+     flags_str = "FS_FILE_FLAGS_HEADER_INCLUDED";
+     break;
+  case(FS_FILE_FLAGS_HEADER_PERSISTENT):
+     flags_str = "FS_FILE_FLAGS_HEADER_PERSISTENT";
+     break;
+  case(FS_FILE_FLAGS_HEADER_INCLUDED | FS_FILE_FLAGS_HEADER_PERSISTENT):
+     flags_str = "FS_FILE_FLAGS_HEADER_INCLUDED | FS_FILE_FLAGS_HEADER_PERSISTENT";
+     break;
+  default:
+     flags_str = "0";
+     break;
+  }
+  fprintf(struct_file, "%s," NEWLINE, flags_str);
+  if (precalcChksum) {
+    fprintf(struct_file, "#if HTTPD_PRECALCULATED_CHECKSUM" NEWLINE);
+    fprintf(struct_file, "%d, chksums_%s," NEWLINE, chksum_count, varname);
+    fprintf(struct_file, "#endif /* HTTPD_PRECALCULATED_CHECKSUM */" NEWLINE);
+  }
+  fprintf(struct_file, "}};" NEWLINE NEWLINE);
+  strcpy(lastFileVar, varname);
+
+  /* write actual file contents */
+  i = 0;
+  fprintf(data_file, NEWLINE "/* raw file data (%d bytes) */" NEWLINE, file_size);
+  process_file_data(data_file, file_data, file_size);
+  fprintf(data_file, "};" NEWLINE NEWLINE);
+  free(file_data);
+  return 0;
+}
+
+int file_write_http_header(FILE *data_file, const char *filename, int file_size, u16_t *http_hdr_len,
+                           u16_t *http_hdr_chksum, u8_t provide_content_len, int is_compressed)
+{
+  int i = 0;
+  int response_type = HTTP_HDR_OK;
+  const char* file_type;
+  const char *cur_string;
+  size_t cur_len;
+  int written = 0;
+  size_t hdr_len = 0;
+  u16_t acc;
+  const char *file_ext;
+  int j;
+  u8_t provide_last_modified = includeLastModified;
+
+  memset(hdr_buf, 0, sizeof(hdr_buf));
+
+  if (useHttp11) {
+    response_type = HTTP_HDR_OK_11;
+  }
+
+  fprintf(data_file, NEWLINE "/* HTTP header */");
+  if (strstr(filename, "404") == filename) {
+    response_type = HTTP_HDR_NOT_FOUND;
+    if (useHttp11) {
+      response_type = HTTP_HDR_NOT_FOUND_11;
+    }
+  } else if (strstr(filename, "400") == filename) {
+    response_type = HTTP_HDR_BAD_REQUEST;
+    if (useHttp11) {
+      response_type = HTTP_HDR_BAD_REQUEST_11;
+    }
+  } else if (strstr(filename, "501") == filename) {
+    response_type = HTTP_HDR_NOT_IMPL;
+    if (useHttp11) {
+      response_type = HTTP_HDR_NOT_IMPL_11;
+    }
+  }
+  cur_string = g_psHTTPHeaderStrings[response_type];
+  cur_len = strlen(cur_string);
+  fprintf(data_file, NEWLINE "/* \"%s\" (%d bytes) */" NEWLINE, cur_string, cur_len);
+  written += file_put_ascii(data_file, cur_string, cur_len, &i);
+  i = 0;
+  if (precalcChksum) {
+    memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
+    hdr_len += cur_len;
+  }
+
+  cur_string = serverID;
+  cur_len = strlen(cur_string);
+  fprintf(data_file, NEWLINE "/* \"%s\" (%d bytes) */" NEWLINE, cur_string, cur_len);
+  written += file_put_ascii(data_file, cur_string, cur_len, &i);
+  i = 0;
+  if (precalcChksum) {
+    memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
+    hdr_len += cur_len;
+  }
+
+  file_ext = filename;
+  if (file_ext != NULL) {
+    while(strstr(file_ext, ".") != NULL) {
+      file_ext = strstr(file_ext, ".");
+      file_ext++;
+    }
+  }
+  if ((file_ext == NULL) || (*file_ext == 0)) {
+    printf("failed to get extension for file \"%s\", using default.\n", filename);
+    file_type = HTTP_HDR_DEFAULT_TYPE;
+  } else {
+    file_type = NULL;
+    for (j = 0; j < NUM_HTTP_HEADERS; j++) {
+      if (!strcmp(file_ext, g_psHTTPHeaders[j].extension)) {
+        file_type = g_psHTTPHeaders[j].content_type;
+        break;
+      }
+    }
+    if (file_type == NULL) {
+      printf("failed to get file type for extension \"%s\", using default.\n", file_ext);
+      file_type = HTTP_HDR_DEFAULT_TYPE;
+    }
+  }
+
+  /* Content-Length is used for persistent connections in HTTP/1.1 but also for
+     download progress in older versions
+     @todo: just use a big-enough buffer and let the HTTPD send spaces? */
+  if (provide_content_len) {
+    char intbuf[MAX_PATH_LEN];
+    int content_len = file_size;
+    memset(intbuf, 0, sizeof(intbuf));
+    cur_string = g_psHTTPHeaderStrings[HTTP_HDR_CONTENT_LENGTH];
+    cur_len = strlen(cur_string);
+    fprintf(data_file, NEWLINE "/* \"%s%d\r\n\" (%d+ bytes) */" NEWLINE, cur_string, content_len, cur_len+2);
+    written += file_put_ascii(data_file, cur_string, cur_len, &i);
+    if (precalcChksum) {
+      memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
+      hdr_len += cur_len;
+    }
+
+    _itoa(content_len, intbuf, 10);
+    strcat(intbuf, "\r\n");
+    cur_len = strlen(intbuf);
+    written += file_put_ascii(data_file, intbuf, cur_len, &i);
+    i = 0;
+    if (precalcChksum) {
+      memcpy(&hdr_buf[hdr_len], intbuf, cur_len);
+      hdr_len += cur_len;
+    }
+  }
+  if (provide_last_modified) {
+    char modbuf[256];
+    struct stat stat_data;
+    struct tm* t;
+    memset(modbuf, 0, sizeof(modbuf));
+    memset(&stat_data, 0, sizeof(stat_data));
+    cur_string = modbuf;
+    strcpy(modbuf, "Last-Modified: ");
+    if (stat(filename, &stat_data) != 0) {
+       printf("stat(%s) failed with error %d\n", filename, errno);
+       exit(-1);
+    }
+    t = gmtime(&stat_data.st_mtime);
+    if (t == NULL) {
+       printf("gmtime() failed with error %d\n", errno);
+       exit(-1);
+    }
+    strftime(&modbuf[15], sizeof(modbuf)-15, "%a, %d %b %Y %H:%M:%S GMT", t);
+    cur_len = strlen(cur_string);
+    fprintf(data_file, NEWLINE "/* \"%s\"\r\n\" (%d+ bytes) */" NEWLINE, cur_string, cur_len+2);
+    written += file_put_ascii(data_file, cur_string, cur_len, &i);
+    if (precalcChksum) {
+      memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
+      hdr_len += cur_len;
+    }
+
+    modbuf[0] = 0;
+    strcat(modbuf, "\r\n");
+    cur_len = strlen(modbuf);
+    written += file_put_ascii(data_file, modbuf, cur_len, &i);
+    i = 0;
+    if (precalcChksum) {
+      memcpy(&hdr_buf[hdr_len], modbuf, cur_len);
+      hdr_len += cur_len;
+    }
+  }
+
+  /* HTTP/1.1 implements persistent connections */
+  if (useHttp11) {
+    if (provide_content_len) {
+      cur_string = g_psHTTPHeaderStrings[HTTP_HDR_CONN_KEEPALIVE];
+    } else {
+      /* no Content-Length available, so a persistent connection is no possible
+         because the client does not know the data length */
+      cur_string = g_psHTTPHeaderStrings[HTTP_HDR_CONN_CLOSE];
+    }
+    cur_len = strlen(cur_string);
+    fprintf(data_file, NEWLINE "/* \"%s\" (%d bytes) */" NEWLINE, cur_string, cur_len);
+    written += file_put_ascii(data_file, cur_string, cur_len, &i);
+    i = 0;
+    if (precalcChksum) {
+      memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
+      hdr_len += cur_len;
+    }
+  }
+
+#if MAKEFS_SUPPORT_DEFLATE
+  if (is_compressed) {
+    /* tell the client about the deflate encoding */
+    LWIP_ASSERT("error", deflateNonSsiFiles);
+    cur_string = "Content-Encoding: deflate\r\n";
+    cur_len = strlen(cur_string);
+    fprintf(data_file, NEWLINE "/* \"%s\" (%d bytes) */" NEWLINE, cur_string, cur_len);
+    written += file_put_ascii(data_file, cur_string, cur_len, &i);
+    i = 0;
+  }
+#else
+  LWIP_UNUSED_ARG(is_compressed);
+#endif
+
+  /* write content-type, ATTENTION: this includes the double-CRLF! */
+  cur_string = file_type;
+  cur_len = strlen(cur_string);
+  fprintf(data_file, NEWLINE "/* \"%s\" (%d bytes) */" NEWLINE, cur_string, cur_len);
+  written += file_put_ascii(data_file, cur_string, cur_len, &i);
+  i = 0;
+
+  /* ATTENTION: headers are done now (double-CRLF has been written!) */
+
+  if (precalcChksum) {
+    memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
+    hdr_len += cur_len;
+
+    LWIP_ASSERT("hdr_len <= 0xffff", hdr_len <= 0xffff);
+    LWIP_ASSERT("strlen(hdr_buf) == hdr_len", strlen(hdr_buf) == hdr_len);
+    acc = ~inet_chksum(hdr_buf, (u16_t)hdr_len);
+    *http_hdr_len = (u16_t)hdr_len;
+    *http_hdr_chksum = acc;
+  }
+
+  return written;
+}
+
+int file_put_ascii(FILE *file, const char* ascii_string, int len, int *i)
+{
+  int x;
+  for (x = 0; x < len; x++) {
+    unsigned char cur = ascii_string[x];
+    fprintf(file, "0x%02.2x,", cur);
+    if ((++(*i) % HEX_BYTES_PER_LINE) == 0) {
+      fprintf(file, NEWLINE);
+    }
+  }
+  return len;
+}
+
+int s_put_ascii(char *buf, const char *ascii_string, int len, int *i)
+{
+  int x;
+  int idx = 0;
+  for (x = 0; x < len; x++) {
+    unsigned char cur = ascii_string[x];
+    sprintf(&buf[idx], "0x%02.2x,", cur);
+    idx += 5;
+    if ((++(*i) % HEX_BYTES_PER_LINE) == 0) {
+      sprintf(&buf[idx], NEWLINE);
+      idx += NEWLINE_LEN;
+    }
+  }
+  return len;
+}

+ 13 - 13
libs/thirdparty/lwip_2.1.2/src/apps/http/makefsdata/readme.txt → libs/thirdparty/LwIP/src/apps/httpd/makefsdata/readme.txt

@@ -1,13 +1,13 @@
-This directory contains a script ('makefsdata') to create C code suitable for
-httpd for given html pages (or other files) in a directory.
-
-There is also a plain C console application doing the same and extended a bit.
-
-Usage: htmlgen [targetdir] [-s] [-i]s
-   targetdir: relative or absolute path to files to convert
-   switch -s: toggle processing of subdirectories (default is on)
-   switch -e: exclude HTTP header from file (header is created at runtime, default is on)
-   switch -11: include HTTP 1.1 header (1.0 is default)
-
-  if targetdir not specified, makefsdata will attempt to
-  process files in subdirectory 'fs'.
+This directory contains a script ('makefsdata') to create C code suitable for
+httpd for given html pages (or other files) in a directory.
+
+There is also a plain C console application doing the same and extended a bit.
+
+Usage: htmlgen [targetdir] [-s] [-i]s
+   targetdir: relative or absolute path to files to convert
+   switch -s: toggle processing of subdirectories (default is on)
+   switch -e: exclude HTTP header from file (header is created at runtime, default is on)
+   switch -11: include HTTP 1.1 header (1.0 is default)
+
+  if targetdir not specified, makefsdata will attempt to
+  process files in subdirectory 'fs'.

+ 661 - 0
libs/thirdparty/LwIP/src/apps/lwiperf/lwiperf.c

@@ -0,0 +1,661 @@
+/**
+ * @file
+ * lwIP iPerf server implementation
+ */
+
+/**
+ * @defgroup iperf Iperf server
+ * @ingroup apps
+ *
+ * This is a simple performance measuring server to check your bandwith using
+ * iPerf2 on a PC as client.
+ * It is currently a minimal implementation providing an IPv4 TCP server only.
+ *
+ * @todo: implement UDP mode and IPv6
+ */
+
+/*
+ * Copyright (c) 2014 Simon Goldschmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt
+ */
+
+#include "lwip/apps/lwiperf.h"
+
+#include "lwip/tcp.h"
+#include "lwip/sys.h"
+
+#include <string.h>
+
+/* Currently, only TCP-over-IPv4 is implemented (does iperf support IPv6 anyway?) */
+#if LWIP_IPV4 && LWIP_TCP && LWIP_CALLBACK_API
+
+/** Specify the idle timeout (in seconds) after that the test fails */
+#ifndef LWIPERF_TCP_MAX_IDLE_SEC
+#define LWIPERF_TCP_MAX_IDLE_SEC    10U
+#endif
+#if LWIPERF_TCP_MAX_IDLE_SEC > 255
+#error LWIPERF_TCP_MAX_IDLE_SEC must fit into an u8_t
+#endif
+
+/* File internal memory allocation (struct lwiperf_*): this defaults to
+   the heap */
+#ifndef LWIPERF_ALLOC
+#define LWIPERF_ALLOC(type)         mem_malloc(sizeof(type))
+#define LWIPERF_FREE(type, item)    mem_free(item)
+#endif
+
+/** If this is 1, check that received data has the correct format */
+#ifndef LWIPERF_CHECK_RX_DATA
+#define LWIPERF_CHECK_RX_DATA       0
+#endif
+
+/** This is the Iperf settings struct sent from the client */
+typedef struct _lwiperf_settings {
+#define LWIPERF_FLAGS_ANSWER_TEST 0x80000000
+#define LWIPERF_FLAGS_ANSWER_NOW  0x00000001
+  u32_t flags;
+  u32_t num_threads; /* unused for now */
+  u32_t remote_port;
+  u32_t buffer_len; /* unused for now */
+  u32_t win_band; /* TCP window / UDP rate: unused for now */
+  u32_t amount; /* pos. value: bytes?; neg. values: time (unit is 10ms: 1/100 second) */
+} lwiperf_settings_t;
+
+/** Basic connection handle */
+struct _lwiperf_state_base;
+typedef struct _lwiperf_state_base lwiperf_state_base_t;
+struct _lwiperf_state_base {
+  /* 1=tcp, 0=udp */
+  u8_t tcp;
+  /* 1=server, 0=client */
+  u8_t server;
+  lwiperf_state_base_t* next;
+  lwiperf_state_base_t* related_server_state;
+};
+
+/** Connection handle for a TCP iperf session */
+typedef struct _lwiperf_state_tcp {
+  lwiperf_state_base_t base;
+  struct tcp_pcb* server_pcb;
+  struct tcp_pcb* conn_pcb;
+  u32_t time_started;
+  lwiperf_report_fn report_fn;
+  void* report_arg;
+  u8_t poll_count;
+  u8_t next_num;
+  u32_t bytes_transferred;
+  lwiperf_settings_t settings;
+  u8_t have_settings_buf;
+} lwiperf_state_tcp_t;
+
+/** List of active iperf sessions */
+static lwiperf_state_base_t* lwiperf_all_connections;
+/** A const buffer to send from: we want to measure sending, not copying! */
+static const u8_t lwiperf_txbuf_const[1600] = {
+  '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+  '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+  '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+  '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+  '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+  '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+  '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+  '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+  '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+  '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+  '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+  '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+  '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+  '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+  '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+  '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+  '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+  '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+  '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+  '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+  '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+  '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+  '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+  '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+  '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+  '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+  '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+  '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+  '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+  '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+  '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+  '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+  '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+  '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+  '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+  '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+  '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+  '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+  '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+  '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+};
+
+static err_t lwiperf_tcp_poll(void *arg, struct tcp_pcb *tpcb);
+static void lwiperf_tcp_err(void *arg, err_t err);
+
+/** Add an iperf session to the 'active' list */
+static void
+lwiperf_list_add(lwiperf_state_base_t* item)
+{
+  if (lwiperf_all_connections == NULL) {
+    lwiperf_all_connections = item;
+  } else {
+    item = lwiperf_all_connections;
+  }
+}
+
+/** Remove an iperf session from the 'active' list */
+static void
+lwiperf_list_remove(lwiperf_state_base_t* item)
+{
+  lwiperf_state_base_t* prev = NULL;
+  lwiperf_state_base_t* iter;
+  for (iter = lwiperf_all_connections; iter != NULL; prev = iter, iter = iter->next) {
+    if (iter == item) {
+      if (prev == NULL) {
+        lwiperf_all_connections = iter->next;
+      } else {
+        prev->next = item;
+      }
+      /* @debug: ensure this item is listed only once */
+      for (iter = iter->next; iter != NULL; iter = iter->next) {
+        LWIP_ASSERT("duplicate entry", iter != item);
+      }
+      break;
+    }
+  }
+}
+
+/** Call the report function of an iperf tcp session */
+static void
+lwip_tcp_conn_report(lwiperf_state_tcp_t* conn, enum lwiperf_report_type report_type)
+{
+  if ((conn != NULL) && (conn->report_fn != NULL)) {
+    u32_t now, duration_ms, bandwidth_kbitpsec;
+    now = sys_now();
+    duration_ms = now - conn->time_started;
+    if (duration_ms == 0) {
+      bandwidth_kbitpsec = 0;
+    } else {
+      bandwidth_kbitpsec = (conn->bytes_transferred / duration_ms) * 8U;
+    }
+    conn->report_fn(conn->report_arg, report_type,
+      &conn->conn_pcb->local_ip, conn->conn_pcb->local_port,
+      &conn->conn_pcb->remote_ip, conn->conn_pcb->remote_port,
+      conn->bytes_transferred, duration_ms, bandwidth_kbitpsec);
+  }
+}
+
+/** Close an iperf tcp session */
+static void
+lwiperf_tcp_close(lwiperf_state_tcp_t* conn, enum lwiperf_report_type report_type)
+{
+  err_t err;
+
+  lwip_tcp_conn_report(conn, report_type);
+  lwiperf_list_remove(&conn->base);
+  if (conn->conn_pcb != NULL) {
+    tcp_arg(conn->conn_pcb, NULL);
+    tcp_poll(conn->conn_pcb, NULL, 0);
+    tcp_sent(conn->conn_pcb, NULL);
+    tcp_recv(conn->conn_pcb, NULL);
+    tcp_err(conn->conn_pcb, NULL);
+    err = tcp_close(conn->conn_pcb);
+    if (err != ERR_OK) {
+      /* don't want to wait for free memory here... */
+      tcp_abort(conn->conn_pcb);
+    }
+  } else {
+    /* no conn pcb, this is the server pcb */
+    err = tcp_close(conn->server_pcb);
+    LWIP_ASSERT("error", err != ERR_OK);
+  }
+  LWIPERF_FREE(lwiperf_state_tcp_t, conn);
+}
+
+/** Try to send more data on an iperf tcp session */
+static err_t
+lwiperf_tcp_client_send_more(lwiperf_state_tcp_t* conn)
+{
+  int send_more;
+  err_t err;
+  u16_t txlen;
+  u16_t txlen_max;
+  void* txptr;
+  u8_t apiflags;
+
+  LWIP_ASSERT("conn invalid", (conn != NULL) && conn->base.tcp && (conn->base.server == 0));
+
+  do {
+    send_more = 0;
+    if (conn->settings.amount & PP_HTONL(0x80000000)) {
+      /* this session is time-limited */
+      u32_t now = sys_now();
+      u32_t diff_ms = now - conn->time_started;
+      u32_t time = (u32_t)-(s32_t)lwip_htonl(conn->settings.amount);
+      u32_t time_ms = time * 10;
+      if (diff_ms >= time_ms) {
+        /* time specified by the client is over -> close the connection */
+        lwiperf_tcp_close(conn, LWIPERF_TCP_DONE_CLIENT);
+        return ERR_OK;
+      }
+    } else {
+      /* this session is byte-limited */
+      u32_t amount_bytes = lwip_htonl(conn->settings.amount);
+      /* @todo: this can send up to 1*MSS more than requested... */
+      if (amount_bytes >= conn->bytes_transferred) {
+        /* all requested bytes transferred -> close the connection */
+        lwiperf_tcp_close(conn, LWIPERF_TCP_DONE_CLIENT);
+        return ERR_OK;
+      }
+    }
+
+    if (conn->bytes_transferred < 24) {
+      /* transmit the settings a first time */
+      txptr = &((u8_t*)&conn->settings)[conn->bytes_transferred];
+      txlen_max = (u16_t)(24 - conn->bytes_transferred);
+      apiflags = TCP_WRITE_FLAG_COPY;
+    } else if (conn->bytes_transferred < 48) {
+      /* transmit the settings a second time */
+      txptr = &((u8_t*)&conn->settings)[conn->bytes_transferred - 24];
+      txlen_max = (u16_t)(48 - conn->bytes_transferred);
+      apiflags = TCP_WRITE_FLAG_COPY | TCP_WRITE_FLAG_MORE;
+      send_more = 1;
+    } else {
+      /* transmit data */
+      /* @todo: every x bytes, transmit the settings again */
+      txptr = LWIP_CONST_CAST(void*, &lwiperf_txbuf_const[conn->bytes_transferred % 10]);
+      txlen_max = TCP_MSS;
+      if (conn->bytes_transferred == 48) { /* @todo: fix this for intermediate settings, too */
+        txlen_max = TCP_MSS - 24;
+      }
+      apiflags = 0; /* no copying needed */
+      send_more = 1;
+    }
+    txlen = txlen_max;
+    do {
+      err = tcp_write(conn->conn_pcb, txptr, txlen, apiflags);
+      if (err ==  ERR_MEM) {
+        txlen /= 2;
+      }
+    } while ((err == ERR_MEM) && (txlen >= (TCP_MSS/2)));
+
+    if (err == ERR_OK) {
+      conn->bytes_transferred += txlen;
+    } else {
+      send_more = 0;
+    }
+  } while(send_more);
+
+  tcp_output(conn->conn_pcb);
+  return ERR_OK;
+}
+
+/** TCP sent callback, try to send more data */
+static err_t
+lwiperf_tcp_client_sent(void *arg, struct tcp_pcb *tpcb, u16_t len)
+{
+  lwiperf_state_tcp_t* conn = (lwiperf_state_tcp_t*)arg;
+  /* @todo: check 'len' (e.g. to time ACK of all data)? for now, we just send more... */
+  LWIP_ASSERT("invalid conn", conn->conn_pcb == tpcb);
+  LWIP_UNUSED_ARG(tpcb);
+  LWIP_UNUSED_ARG(len);
+
+  conn->poll_count = 0;
+
+  return lwiperf_tcp_client_send_more(conn);
+}
+
+/** TCP connected callback (active connection), send data now */
+static err_t
+lwiperf_tcp_client_connected(void *arg, struct tcp_pcb *tpcb, err_t err)
+{
+  lwiperf_state_tcp_t* conn = (lwiperf_state_tcp_t*)arg;
+  LWIP_ASSERT("invalid conn", conn->conn_pcb == tpcb);
+  LWIP_UNUSED_ARG(tpcb);
+  if (err != ERR_OK) {
+    lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_REMOTE);
+    return ERR_OK;
+  }
+  conn->poll_count = 0;
+  conn->time_started = sys_now();
+  return lwiperf_tcp_client_send_more(conn);
+}
+
+/** Start TCP connection back to the client (either parallel or after the
+ * receive test has finished.
+ */
+static err_t
+lwiperf_tx_start(lwiperf_state_tcp_t* conn)
+{
+  err_t err;
+  lwiperf_state_tcp_t* client_conn;
+  struct tcp_pcb* newpcb;
+  ip_addr_t remote_addr;
+  u16_t remote_port;
+
+  client_conn = (lwiperf_state_tcp_t*)LWIPERF_ALLOC(lwiperf_state_tcp_t);
+  if (client_conn == NULL) {
+    return ERR_MEM;
+  }
+  newpcb = tcp_new();
+  if (newpcb == NULL) {
+    LWIPERF_FREE(lwiperf_state_tcp_t, client_conn);
+    return ERR_MEM;
+  }
+
+  MEMCPY(client_conn, conn, sizeof(lwiperf_state_tcp_t));
+  client_conn->base.server = 0;
+  client_conn->server_pcb = NULL;
+  client_conn->conn_pcb = newpcb;
+  client_conn->time_started = sys_now(); /* @todo: set this again on 'connected' */
+  client_conn->poll_count = 0;
+  client_conn->next_num = 4; /* initial nr is '4' since the header has 24 byte */
+  client_conn->bytes_transferred = 0;
+  client_conn->settings.flags = 0; /* prevent the remote side starting back as client again */
+
+  tcp_arg(newpcb, client_conn);
+  tcp_sent(newpcb, lwiperf_tcp_client_sent);
+  tcp_poll(newpcb, lwiperf_tcp_poll, 2U);
+  tcp_err(newpcb, lwiperf_tcp_err);
+
+  ip_addr_copy(remote_addr, conn->conn_pcb->remote_ip);
+  remote_port = (u16_t)lwip_htonl(client_conn->settings.remote_port);
+
+  err = tcp_connect(newpcb, &remote_addr, remote_port, lwiperf_tcp_client_connected);
+  if (err != ERR_OK) {
+    lwiperf_tcp_close(client_conn, LWIPERF_TCP_ABORTED_LOCAL);
+    return err;
+  }
+  lwiperf_list_add(&client_conn->base);
+  return ERR_OK;
+}
+
+/** Receive data on an iperf tcp session */
+static err_t
+lwiperf_tcp_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
+{
+  u8_t tmp;
+  u16_t tot_len;
+  u32_t packet_idx;
+  struct pbuf* q;
+  lwiperf_state_tcp_t* conn = (lwiperf_state_tcp_t*)arg;
+
+  LWIP_ASSERT("pcb mismatch", conn->conn_pcb == tpcb);
+  LWIP_UNUSED_ARG(tpcb);
+
+  if (err != ERR_OK) {
+    lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_REMOTE);
+    return ERR_OK;
+  }
+  if (p == NULL) {
+    /* connection closed -> test done */
+    if ((conn->settings.flags & PP_HTONL(LWIPERF_FLAGS_ANSWER_TEST|LWIPERF_FLAGS_ANSWER_NOW)) ==
+        PP_HTONL(LWIPERF_FLAGS_ANSWER_TEST)) {
+      /* client requested transmission after end of test */
+      lwiperf_tx_start(conn);
+    }
+    lwiperf_tcp_close(conn, LWIPERF_TCP_DONE_SERVER);
+    return ERR_OK;
+  }
+  tot_len = p->tot_len;
+
+  conn->poll_count = 0;
+
+  if ((!conn->have_settings_buf) || ((conn->bytes_transferred -24) % (1024*128) == 0)) {
+    /* wait for 24-byte header */
+    if (p->tot_len < sizeof(lwiperf_settings_t)) {
+      lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL_DATAERROR);
+      pbuf_free(p);
+      return ERR_VAL;
+    }
+    if (!conn->have_settings_buf) {
+      if (pbuf_copy_partial(p, &conn->settings, sizeof(lwiperf_settings_t), 0) != sizeof(lwiperf_settings_t)) {
+        lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL);
+        pbuf_free(p);
+        return ERR_VAL;
+      }
+      conn->have_settings_buf = 1;
+      if ((conn->settings.flags & PP_HTONL(LWIPERF_FLAGS_ANSWER_TEST|LWIPERF_FLAGS_ANSWER_NOW)) ==
+        PP_HTONL(LWIPERF_FLAGS_ANSWER_TEST|LWIPERF_FLAGS_ANSWER_NOW)) {
+          /* client requested parallel transmission test */
+          err_t err2 = lwiperf_tx_start(conn);
+          if (err2 != ERR_OK) {
+            lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL_TXERROR);
+            pbuf_free(p);
+            return err2;
+          }
+      }
+    } else {
+      if (pbuf_memcmp(p, 0, &conn->settings, sizeof(lwiperf_settings_t)) != 0) {
+        lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL_DATAERROR);
+        pbuf_free(p);
+        return ERR_VAL;
+      }
+    }
+    conn->bytes_transferred += sizeof(lwiperf_settings_t);
+    if (conn->bytes_transferred <= 24) {
+      conn->time_started = sys_now();
+      tcp_recved(tpcb, p->tot_len);
+      pbuf_free(p);
+      return ERR_OK;
+    }
+    conn->next_num = 4; /* 24 bytes received... */
+    tmp = pbuf_header(p, -24);
+    LWIP_ASSERT("pbuf_header failed", tmp == 0);
+  }
+
+  packet_idx = 0;
+  for (q = p; q != NULL; q = q->next) {
+#if LWIPERF_CHECK_RX_DATA
+    const u8_t* payload = (const u8_t*)q->payload;
+    u16_t i;
+    for (i = 0; i < q->len; i++) {
+      u8_t val = payload[i];
+      u8_t num = val - '0';
+      if (num == conn->next_num) {
+        conn->next_num++;
+        if (conn->next_num == 10) {
+          conn->next_num = 0;
+        }
+      } else {
+        lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL_DATAERROR);
+        pbuf_free(p);
+        return ERR_VAL;
+      }
+    }
+#endif
+    packet_idx += q->len;
+  }
+  LWIP_ASSERT("count mismatch", packet_idx == p->tot_len);
+  conn->bytes_transferred += packet_idx;
+  tcp_recved(tpcb, tot_len);
+  pbuf_free(p);
+  return ERR_OK;
+}
+
+/** Error callback, iperf tcp session aborted */
+static void
+lwiperf_tcp_err(void *arg, err_t err)
+{
+  lwiperf_state_tcp_t* conn = (lwiperf_state_tcp_t*)arg;
+  LWIP_UNUSED_ARG(err);
+  lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_REMOTE);
+}
+
+/** TCP poll callback, try to send more data */
+static err_t
+lwiperf_tcp_poll(void *arg, struct tcp_pcb *tpcb)
+{
+  lwiperf_state_tcp_t* conn = (lwiperf_state_tcp_t*)arg;
+  LWIP_ASSERT("pcb mismatch", conn->conn_pcb == tpcb);
+  LWIP_UNUSED_ARG(tpcb);
+  if (++conn->poll_count >= LWIPERF_TCP_MAX_IDLE_SEC) {
+    lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL);
+    return ERR_OK; /* lwiperf_tcp_close frees conn */
+  }
+
+  if (!conn->base.server) {
+    lwiperf_tcp_client_send_more(conn);
+  }
+
+  return ERR_OK;
+}
+
+/** This is called when a new client connects for an iperf tcp session */
+static err_t
+lwiperf_tcp_accept(void *arg, struct tcp_pcb *newpcb, err_t err)
+{
+  lwiperf_state_tcp_t *s, *conn;
+  if ((err != ERR_OK) || (newpcb == NULL) || (arg == NULL)) {
+    return ERR_VAL;
+  }
+
+  s = (lwiperf_state_tcp_t*)arg;
+  conn = (lwiperf_state_tcp_t*)LWIPERF_ALLOC(lwiperf_state_tcp_t);
+  if (conn == NULL) {
+    return ERR_MEM;
+  }
+  memset(conn, 0, sizeof(lwiperf_state_tcp_t));
+  conn->base.tcp = 1;
+  conn->base.server = 1;
+  conn->base.related_server_state = &s->base;
+  conn->server_pcb = s->server_pcb;
+  conn->conn_pcb = newpcb;
+  conn->time_started = sys_now();
+  conn->report_fn = s->report_fn;
+  conn->report_arg = s->report_arg;
+
+  /* setup the tcp rx connection */
+  tcp_arg(newpcb, conn);
+  tcp_recv(newpcb, lwiperf_tcp_recv);
+  tcp_poll(newpcb, lwiperf_tcp_poll, 2U);
+  tcp_err(conn->conn_pcb, lwiperf_tcp_err);
+
+  lwiperf_list_add(&conn->base);
+  return ERR_OK;
+}
+
+/** 
+ * @ingroup iperf
+ * Start a TCP iperf server on the default TCP port (5001) and listen for
+ * incoming connections from iperf clients.
+ *
+ * @returns a connection handle that can be used to abort the server
+ *          by calling @ref lwiperf_abort()
+ */
+void*
+lwiperf_start_tcp_server_default(lwiperf_report_fn report_fn, void* report_arg)
+{
+  return lwiperf_start_tcp_server(IP_ADDR_ANY, LWIPERF_TCP_PORT_DEFAULT,
+    report_fn, report_arg);
+}
+
+/**
+ * @ingroup iperf
+ * Start a TCP iperf server on a specific IP address and port and listen for
+ * incoming connections from iperf clients.
+ *
+ * @returns a connection handle that can be used to abort the server
+ *          by calling @ref lwiperf_abort()
+ */
+void*
+lwiperf_start_tcp_server(const ip_addr_t* local_addr, u16_t local_port,
+  lwiperf_report_fn report_fn, void* report_arg)
+{
+  err_t err;
+  struct tcp_pcb* pcb;
+  lwiperf_state_tcp_t* s;
+
+  if (local_addr == NULL) {
+    return NULL;
+  }
+
+  s = (lwiperf_state_tcp_t*)LWIPERF_ALLOC(lwiperf_state_tcp_t);
+  if (s == NULL) {
+    return NULL;
+  }
+  memset(s, 0, sizeof(lwiperf_state_tcp_t));
+  s->base.tcp = 1;
+  s->base.server = 1;
+  s->report_fn = report_fn;
+  s->report_arg = report_arg;
+
+  pcb = tcp_new();
+  if (pcb != NULL) {
+    err = tcp_bind(pcb, local_addr, local_port);
+    if (err == ERR_OK) {
+      s->server_pcb = tcp_listen_with_backlog(pcb, 1);
+    }
+  }
+  if (s->server_pcb == NULL) {
+    if (pcb != NULL) {
+      tcp_close(pcb);
+    }
+    LWIPERF_FREE(lwiperf_state_tcp_t, s);
+    return NULL;
+  }
+  pcb = NULL;
+
+  tcp_arg(s->server_pcb, s);
+  tcp_accept(s->server_pcb, lwiperf_tcp_accept);
+
+  lwiperf_list_add(&s->base);
+  return s;
+}
+
+/**
+ * @ingroup iperf
+ * Abort an iperf session (handle returned by lwiperf_start_tcp_server*())
+ */
+void
+lwiperf_abort(void* lwiperf_session)
+{
+  lwiperf_state_base_t* i, *dealloc, *last = NULL;
+
+  for (i = lwiperf_all_connections; i != NULL; ) {
+    if ((i == lwiperf_session) || (i->related_server_state == lwiperf_session)) {
+      dealloc = i;
+      i = i->next;
+      if (last != NULL) {
+        last->next = i;
+      }
+      LWIPERF_FREE(lwiperf_state_tcp_t, dealloc); /* @todo: type? */
+    } else {
+      last = i;
+      i = i->next;
+    }
+  }
+}
+
+#endif /* LWIP_IPV4 && LWIP_TCP && LWIP_CALLBACK_API */

+ 2028 - 2407
libs/thirdparty/lwip_2.1.2/src/apps/mdns/mdns.c → libs/thirdparty/LwIP/src/apps/mdns/mdns.c

@@ -1,2407 +1,2028 @@
-/**
- * @file
- * MDNS responder implementation
- *
- * @defgroup mdns MDNS
- * @ingroup apps
- *
- * RFC 6762 - Multicast DNS\n
- * RFC 6763 - DNS-Based Service Discovery\n
- *
- * @verbinclude mdns.txt
- *
- * Things left to implement:
- * -------------------------
- *
- * - Tiebreaking for simultaneous probing
- * - Sending goodbye messages (zero ttl) - shutdown, DHCP lease about to expire, DHCP turned off...
- * - Checking that source address of unicast requests are on the same network
- * - Limiting multicast responses to 1 per second per resource record
- * - Fragmenting replies if required
- * - Handling multi-packet known answers
- * - Individual known answer detection for all local IPv6 addresses
- * - Dynamic size of outgoing packet
- */
-
-/*
- * Copyright (c) 2015 Verisure Innovation AB
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- *
- * Author: Erik Ekman <erik@kryo.se>
- *
- */
-
-#include "lwip/apps/mdns.h"
-#include "lwip/apps/mdns_priv.h"
-#include "lwip/netif.h"
-#include "lwip/udp.h"
-#include "lwip/ip_addr.h"
-#include "lwip/mem.h"
-#include "lwip/prot/dns.h"
-#include "lwip/prot/iana.h"
-#include "lwip/timeouts.h"
-
-#include <string.h>
-
-#if LWIP_MDNS_RESPONDER
-
-#if (LWIP_IPV4 && !LWIP_IGMP)
-#error "If you want to use MDNS with IPv4, you have to define LWIP_IGMP=1 in your lwipopts.h"
-#endif
-#if (LWIP_IPV6 && !LWIP_IPV6_MLD)
-#error "If you want to use MDNS with IPv6, you have to define LWIP_IPV6_MLD=1 in your lwipopts.h"
-#endif
-#if (!LWIP_UDP)
-#error "If you want to use MDNS, you have to define LWIP_UDP=1 in your lwipopts.h"
-#endif
-
-#if LWIP_IPV4
-#include "lwip/igmp.h"
-/* IPv4 multicast group 224.0.0.251 */
-static const ip_addr_t v4group = DNS_MQUERY_IPV4_GROUP_INIT;
-#endif
-
-#if LWIP_IPV6
-#include "lwip/mld6.h"
-/* IPv6 multicast group FF02::FB */
-static const ip_addr_t v6group = DNS_MQUERY_IPV6_GROUP_INIT;
-#endif
-
-#define MDNS_TTL  255
-
-/* Stored offsets to beginning of domain names
- * Used for compression.
- */
-#define NUM_DOMAIN_OFFSETS 10
-#define DOMAIN_JUMP_SIZE 2
-#define DOMAIN_JUMP 0xc000
-
-static u8_t mdns_netif_client_id;
-static struct udp_pcb *mdns_pcb;
-#if MDNS_RESP_USENETIF_EXTCALLBACK
-NETIF_DECLARE_EXT_CALLBACK(netif_callback)
-#endif
-static mdns_name_result_cb_t mdns_name_result_cb;
-
-#define NETIF_TO_HOST(netif) (struct mdns_host*)(netif_get_client_data(netif, mdns_netif_client_id))
-
-#define TOPDOMAIN_LOCAL "local"
-
-#define REVERSE_PTR_TOPDOMAIN "arpa"
-#define REVERSE_PTR_V4_DOMAIN "in-addr"
-#define REVERSE_PTR_V6_DOMAIN "ip6"
-
-#define SRV_PRIORITY 0
-#define SRV_WEIGHT   0
-
-/* Payload size allocated for each outgoing UDP packet */
-#define OUTPACKET_SIZE 500
-
-/* Lookup from hostname -> IPv4 */
-#define REPLY_HOST_A            0x01
-/* Lookup from IPv4/v6 -> hostname */
-#define REPLY_HOST_PTR_V4       0x02
-/* Lookup from hostname -> IPv6 */
-#define REPLY_HOST_AAAA         0x04
-/* Lookup from hostname -> IPv6 */
-#define REPLY_HOST_PTR_V6       0x08
-
-/* Lookup for service types */
-#define REPLY_SERVICE_TYPE_PTR  0x10
-/* Lookup for instances of service */
-#define REPLY_SERVICE_NAME_PTR  0x20
-/* Lookup for location of service instance */
-#define REPLY_SERVICE_SRV       0x40
-/* Lookup for text info on service instance */
-#define REPLY_SERVICE_TXT       0x80
-
-#define MDNS_PROBE_DELAY_MS       250
-#define MDNS_PROBE_COUNT          3
-#ifdef LWIP_RAND
-/* first probe timeout SHOULD be random 0-250 ms*/
-#define MDNS_INITIAL_PROBE_DELAY_MS (LWIP_RAND() % MDNS_PROBE_DELAY_MS)
-#else
-#define MDNS_INITIAL_PROBE_DELAY_MS MDNS_PROBE_DELAY_MS
-#endif
-
-#define MDNS_PROBING_NOT_STARTED  0
-#define MDNS_PROBING_ONGOING      1
-#define MDNS_PROBING_COMPLETE     2
-
-static const char *dnssd_protos[] = {
-  "_udp", /* DNSSD_PROTO_UDP */
-  "_tcp", /* DNSSD_PROTO_TCP */
-};
-
-/** Description of a service */
-struct mdns_service {
-  /** TXT record to answer with */
-  struct mdns_domain txtdata;
-  /** Name of service, like 'myweb' */
-  char name[MDNS_LABEL_MAXLEN + 1];
-  /** Type of service, like '_http' */
-  char service[MDNS_LABEL_MAXLEN + 1];
-  /** Callback function and userdata
-   * to update txtdata buffer */
-  service_get_txt_fn_t txt_fn;
-  void *txt_userdata;
-  /** TTL in seconds of SRV/TXT replies */
-  u32_t dns_ttl;
-  /** Protocol, TCP or UDP */
-  u16_t proto;
-  /** Port of the service */
-  u16_t port;
-};
-
-/** Description of a host/netif */
-struct mdns_host {
-  /** Hostname */
-  char name[MDNS_LABEL_MAXLEN + 1];
-  /** Pointer to services */
-  struct mdns_service *services[MDNS_MAX_SERVICES];
-  /** TTL in seconds of A/AAAA/PTR replies */
-  u32_t dns_ttl;
-  /** Number of probes sent for the current name */
-  u8_t probes_sent;
-  /** State in probing sequence */
-  u8_t probing_state;
-};
-
-/** Information about received packet */
-struct mdns_packet {
-  /** Sender IP/port */
-  ip_addr_t source_addr;
-  u16_t source_port;
-  /** If packet was received unicast */
-  u16_t recv_unicast;
-  /** Netif that received the packet */
-  struct netif *netif;
-  /** Packet data */
-  struct pbuf *pbuf;
-  /** Current parsing offset in packet */
-  u16_t parse_offset;
-  /** Identifier. Used in legacy queries */
-  u16_t tx_id;
-  /** Number of questions in packet,
-   *  read from packet header */
-  u16_t questions;
-  /** Number of unparsed questions */
-  u16_t questions_left;
-  /** Number of answers in packet,
-   *  (sum of normal, authoritative and additional answers)
-   *  read from packet header */
-  u16_t answers;
-  /** Number of unparsed answers */
-  u16_t answers_left;
-};
-
-/** Information about outgoing packet */
-struct mdns_outpacket {
-  /** Netif to send the packet on */
-  struct netif *netif;
-  /** Packet data */
-  struct pbuf *pbuf;
-  /** Current write offset in packet */
-  u16_t write_offset;
-  /** Identifier. Used in legacy queries */
-  u16_t tx_id;
-  /** Destination IP/port if sent unicast */
-  ip_addr_t dest_addr;
-  u16_t dest_port;
-  /** Number of questions written */
-  u16_t questions;
-  /** Number of normal answers written */
-  u16_t answers;
-  /** Number of authoritative answers written */
-  u16_t authoritative;
-  /** Number of additional answers written */
-  u16_t additional;
-  /** Offsets for written domain names in packet.
-   *  Used for compression */
-  u16_t domain_offsets[NUM_DOMAIN_OFFSETS];
-  /** If all answers in packet should set cache_flush bit */
-  u8_t cache_flush;
-  /** If reply should be sent unicast */
-  u8_t unicast_reply;
-  /** If legacy query. (tx_id needed, and write
-   *  question again in reply before answer) */
-  u8_t legacy_query;
-  /* Reply bitmask for host information */
-  u8_t host_replies;
-  /* Bitmask for which reverse IPv6 hosts to answer */
-  u8_t host_reverse_v6_replies;
-  /* Reply bitmask per service */
-  u8_t serv_replies[MDNS_MAX_SERVICES];
-};
-
-/** Domain, type and class.
- *  Shared between questions and answers */
-struct mdns_rr_info {
-  struct mdns_domain domain;
-  u16_t type;
-  u16_t klass;
-};
-
-struct mdns_question {
-  struct mdns_rr_info info;
-  /** unicast reply requested */
-  u16_t unicast;
-};
-
-struct mdns_answer {
-  struct mdns_rr_info info;
-  /** cache flush command bit */
-  u16_t cache_flush;
-  /* Validity time in seconds */
-  u32_t ttl;
-  /** Length of variable answer */
-  u16_t rd_length;
-  /** Offset of start of variable answer in packet */
-  u16_t rd_offset;
-};
-
-static err_t mdns_send_outpacket(struct mdns_outpacket *outpkt, u8_t flags);
-static void mdns_probe(void* arg);
-
-static err_t
-mdns_domain_add_label_base(struct mdns_domain *domain, u8_t len)
-{
-  if (len > MDNS_LABEL_MAXLEN) {
-    return ERR_VAL;
-  }
-  if (len > 0 && (1 + len + domain->length >= MDNS_DOMAIN_MAXLEN)) {
-    return ERR_VAL;
-  }
-  /* Allow only zero marker on last byte */
-  if (len == 0 && (1 + domain->length > MDNS_DOMAIN_MAXLEN)) {
-    return ERR_VAL;
-  }
-  domain->name[domain->length] = len;
-  domain->length++;
-  return ERR_OK;
-}
-
-/**
- * Add a label part to a domain
- * @param domain The domain to add a label to
- * @param label The label to add, like &lt;hostname&gt;, 'local', 'com' or ''
- * @param len The length of the label
- * @return ERR_OK on success, an err_t otherwise if label too long
- */
-err_t
-mdns_domain_add_label(struct mdns_domain *domain, const char *label, u8_t len)
-{
-  err_t err = mdns_domain_add_label_base(domain, len);
-  if (err != ERR_OK) {
-    return err;
-  }
-  if (len) {
-    MEMCPY(&domain->name[domain->length], label, len);
-    domain->length += len;
-  }
-  return ERR_OK;
-}
-
-/**
- * Add a label part to a domain (@see mdns_domain_add_label but copy directly from pbuf)
- */
-static err_t
-mdns_domain_add_label_pbuf(struct mdns_domain *domain, const struct pbuf *p, u16_t offset, u8_t len)
-{
-  err_t err = mdns_domain_add_label_base(domain, len);
-  if (err != ERR_OK) {
-    return err;
-  }
-  if (len) {
-    if (pbuf_copy_partial(p, &domain->name[domain->length], len, offset) != len) {
-      /* take back the ++ done before */
-      domain->length--;
-      return ERR_ARG;
-    }
-    domain->length += len;
-  }
-  return ERR_OK;
-}
-
-/**
- * Internal readname function with max 6 levels of recursion following jumps
- * while decompressing name
- */
-static u16_t
-mdns_readname_loop(struct pbuf *p, u16_t offset, struct mdns_domain *domain, unsigned depth)
-{
-  u8_t c;
-
-  do {
-    if (depth > 5) {
-      /* Too many jumps */
-      return MDNS_READNAME_ERROR;
-    }
-
-    c = pbuf_get_at(p, offset);
-    offset++;
-
-    /* is this a compressed label? */
-    if ((c & 0xc0) == 0xc0) {
-      u16_t jumpaddr;
-      if (offset >= p->tot_len) {
-        /* Make sure both jump bytes fit in the packet */
-        return MDNS_READNAME_ERROR;
-      }
-      jumpaddr = (((c & 0x3f) << 8) | (pbuf_get_at(p, offset) & 0xff));
-      offset++;
-      if (jumpaddr >= SIZEOF_DNS_HDR && jumpaddr < p->tot_len) {
-        u16_t res;
-        /* Recursive call, maximum depth will be checked */
-        res = mdns_readname_loop(p, jumpaddr, domain, depth + 1);
-        /* Dont return offset since new bytes were not read (jumped to somewhere in packet) */
-        if (res == MDNS_READNAME_ERROR) {
-          return res;
-        }
-      } else {
-        return MDNS_READNAME_ERROR;
-      }
-      break;
-    }
-
-    /* normal label */
-    if (c <= MDNS_LABEL_MAXLEN) {
-      err_t res;
-
-      if (c + domain->length >= MDNS_DOMAIN_MAXLEN) {
-        return MDNS_READNAME_ERROR;
-      }
-      res = mdns_domain_add_label_pbuf(domain, p, offset, c);
-      if (res != ERR_OK) {
-        return MDNS_READNAME_ERROR;
-      }
-      offset += c;
-    } else {
-      /* bad length byte */
-      return MDNS_READNAME_ERROR;
-    }
-  } while (c != 0);
-
-  return offset;
-}
-
-/**
- * Read possibly compressed domain name from packet buffer
- * @param p The packet
- * @param offset start position of domain name in packet
- * @param domain The domain name destination
- * @return The new offset after the domain, or MDNS_READNAME_ERROR
- *         if reading failed
- */
-u16_t
-mdns_readname(struct pbuf *p, u16_t offset, struct mdns_domain *domain)
-{
-  memset(domain, 0, sizeof(struct mdns_domain));
-  return mdns_readname_loop(p, offset, domain, 0);
-}
-
-/**
- * Print domain name to debug output
- * @param domain The domain name
- */
-static void
-mdns_domain_debug_print(struct mdns_domain *domain)
-{
-  u8_t *src = domain->name;
-  u8_t i;
-
-  while (*src) {
-    u8_t label_len = *src;
-    src++;
-    for (i = 0; i < label_len; i++) {
-      LWIP_DEBUGF(MDNS_DEBUG, ("%c", src[i]));
-    }
-    src += label_len;
-    LWIP_DEBUGF(MDNS_DEBUG, ("."));
-  }
-}
-
-/**
- * Return 1 if contents of domains match (case-insensitive)
- * @param a Domain name to compare 1
- * @param b Domain name to compare 2
- * @return 1 if domains are equal ignoring case, 0 otherwise
- */
-int
-mdns_domain_eq(struct mdns_domain *a, struct mdns_domain *b)
-{
-  u8_t *ptra, *ptrb;
-  u8_t len;
-  int res;
-
-  if (a->length != b->length) {
-    return 0;
-  }
-
-  ptra = a->name;
-  ptrb = b->name;
-  while (*ptra && *ptrb && ptra < &a->name[a->length]) {
-    if (*ptra != *ptrb) {
-      return 0;
-    }
-    len = *ptra;
-    ptra++;
-    ptrb++;
-    res = lwip_strnicmp((char *) ptra, (char *) ptrb, len);
-    if (res != 0) {
-      return 0;
-    }
-    ptra += len;
-    ptrb += len;
-  }
-  if (*ptra != *ptrb && ptra < &a->name[a->length]) {
-    return 0;
-  }
-  return 1;
-}
-
-/**
- * Call user supplied function to setup TXT data
- * @param service The service to build TXT record for
- */
-static void
-mdns_prepare_txtdata(struct mdns_service *service)
-{
-  memset(&service->txtdata, 0, sizeof(struct mdns_domain));
-  if (service->txt_fn) {
-    service->txt_fn(service, service->txt_userdata);
-  }
-}
-
-#if LWIP_IPV4
-/**
- * Build domain for reverse lookup of IPv4 address
- * like 12.0.168.192.in-addr.arpa. for 192.168.0.12
- * @param domain Where to write the domain name
- * @param addr Pointer to an IPv4 address to encode
- * @return ERR_OK if domain was written, an err_t otherwise
- */
-static err_t
-mdns_build_reverse_v4_domain(struct mdns_domain *domain, const ip4_addr_t *addr)
-{
-  int i;
-  err_t res;
-  const u8_t *ptr;
-
-  LWIP_UNUSED_ARG(res);
-  if (!domain || !addr) {
-    return ERR_ARG;
-  }
-  memset(domain, 0, sizeof(struct mdns_domain));
-  ptr = (const u8_t *) addr;
-  for (i = sizeof(ip4_addr_t) - 1; i >= 0; i--) {
-    char buf[4];
-    u8_t val = ptr[i];
-
-    lwip_itoa(buf, sizeof(buf), val);
-    res = mdns_domain_add_label(domain, buf, (u8_t)strlen(buf));
-    LWIP_ERROR("mdns_build_reverse_v4_domain: Failed to add label", (res == ERR_OK), return res);
-  }
-  res = mdns_domain_add_label(domain, REVERSE_PTR_V4_DOMAIN, (u8_t)(sizeof(REVERSE_PTR_V4_DOMAIN) - 1));
-  LWIP_ERROR("mdns_build_reverse_v4_domain: Failed to add label", (res == ERR_OK), return res);
-  res = mdns_domain_add_label(domain, REVERSE_PTR_TOPDOMAIN, (u8_t)(sizeof(REVERSE_PTR_TOPDOMAIN) - 1));
-  LWIP_ERROR("mdns_build_reverse_v4_domain: Failed to add label", (res == ERR_OK), return res);
-  res = mdns_domain_add_label(domain, NULL, 0);
-  LWIP_ERROR("mdns_build_reverse_v4_domain: Failed to add label", (res == ERR_OK), return res);
-
-  return ERR_OK;
-}
-#endif
-
-#if LWIP_IPV6
-/**
- * Build domain for reverse lookup of IP address
- * like b.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa. for 2001:db8::567:89ab
- * @param domain Where to write the domain name
- * @param addr Pointer to an IPv6 address to encode
- * @return ERR_OK if domain was written, an err_t otherwise
- */
-static err_t
-mdns_build_reverse_v6_domain(struct mdns_domain *domain, const ip6_addr_t *addr)
-{
-  int i;
-  err_t res;
-  const u8_t *ptr;
-  LWIP_UNUSED_ARG(res);
-  if (!domain || !addr) {
-    return ERR_ARG;
-  }
-  memset(domain, 0, sizeof(struct mdns_domain));
-  ptr = (const u8_t *) addr;
-  for (i = sizeof(ip6_addr_p_t) - 1; i >= 0; i--) {
-    char buf;
-    u8_t byte = ptr[i];
-    int j;
-    for (j = 0; j < 2; j++) {
-      if ((byte & 0x0F) < 0xA) {
-        buf = '0' + (byte & 0x0F);
-      } else {
-        buf = 'a' + (byte & 0x0F) - 0xA;
-      }
-      res = mdns_domain_add_label(domain, &buf, sizeof(buf));
-      LWIP_ERROR("mdns_build_reverse_v6_domain: Failed to add label", (res == ERR_OK), return res);
-      byte >>= 4;
-    }
-  }
-  res = mdns_domain_add_label(domain, REVERSE_PTR_V6_DOMAIN, (u8_t)(sizeof(REVERSE_PTR_V6_DOMAIN) - 1));
-  LWIP_ERROR("mdns_build_reverse_v6_domain: Failed to add label", (res == ERR_OK), return res);
-  res = mdns_domain_add_label(domain, REVERSE_PTR_TOPDOMAIN, (u8_t)(sizeof(REVERSE_PTR_TOPDOMAIN) - 1));
-  LWIP_ERROR("mdns_build_reverse_v6_domain: Failed to add label", (res == ERR_OK), return res);
-  res = mdns_domain_add_label(domain, NULL, 0);
-  LWIP_ERROR("mdns_build_reverse_v6_domain: Failed to add label", (res == ERR_OK), return res);
-
-  return ERR_OK;
-}
-#endif
-
-/* Add .local. to domain */
-static err_t
-mdns_add_dotlocal(struct mdns_domain *domain)
-{
-  err_t res = mdns_domain_add_label(domain, TOPDOMAIN_LOCAL, (u8_t)(sizeof(TOPDOMAIN_LOCAL) - 1));
-  LWIP_UNUSED_ARG(res);
-  LWIP_ERROR("mdns_add_dotlocal: Failed to add label", (res == ERR_OK), return res);
-  return mdns_domain_add_label(domain, NULL, 0);
-}
-
-/**
- * Build the <hostname>.local. domain name
- * @param domain Where to write the domain name
- * @param mdns TMDNS netif descriptor.
- * @return ERR_OK if domain <hostname>.local. was written, an err_t otherwise
- */
-static err_t
-mdns_build_host_domain(struct mdns_domain *domain, struct mdns_host *mdns)
-{
-  err_t res;
-  LWIP_UNUSED_ARG(res);
-  memset(domain, 0, sizeof(struct mdns_domain));
-  LWIP_ERROR("mdns_build_host_domain: mdns != NULL", (mdns != NULL), return ERR_VAL);
-  res = mdns_domain_add_label(domain, mdns->name, (u8_t)strlen(mdns->name));
-  LWIP_ERROR("mdns_build_host_domain: Failed to add label", (res == ERR_OK), return res);
-  return mdns_add_dotlocal(domain);
-}
-
-/**
- * Build the lookup-all-services special DNS-SD domain name
- * @param domain Where to write the domain name
- * @return ERR_OK if domain _services._dns-sd._udp.local. was written, an err_t otherwise
- */
-static err_t
-mdns_build_dnssd_domain(struct mdns_domain *domain)
-{
-  err_t res;
-  LWIP_UNUSED_ARG(res);
-  memset(domain, 0, sizeof(struct mdns_domain));
-  res = mdns_domain_add_label(domain, "_services", (u8_t)(sizeof("_services") - 1));
-  LWIP_ERROR("mdns_build_dnssd_domain: Failed to add label", (res == ERR_OK), return res);
-  res = mdns_domain_add_label(domain, "_dns-sd", (u8_t)(sizeof("_dns-sd") - 1));
-  LWIP_ERROR("mdns_build_dnssd_domain: Failed to add label", (res == ERR_OK), return res);
-  res = mdns_domain_add_label(domain, dnssd_protos[DNSSD_PROTO_UDP], (u8_t)strlen(dnssd_protos[DNSSD_PROTO_UDP]));
-  LWIP_ERROR("mdns_build_dnssd_domain: Failed to add label", (res == ERR_OK), return res);
-  return mdns_add_dotlocal(domain);
-}
-
-/**
- * Build domain name for a service
- * @param domain Where to write the domain name
- * @param service The service struct, containing service name, type and protocol
- * @param include_name Whether to include the service name in the domain
- * @return ERR_OK if domain was written. If service name is included,
- *         <name>.<type>.<proto>.local. will be written, otherwise <type>.<proto>.local.
- *         An err_t is returned on error.
- */
-static err_t
-mdns_build_service_domain(struct mdns_domain *domain, struct mdns_service *service, int include_name)
-{
-  err_t res;
-  LWIP_UNUSED_ARG(res);
-  memset(domain, 0, sizeof(struct mdns_domain));
-  if (include_name) {
-    res = mdns_domain_add_label(domain, service->name, (u8_t)strlen(service->name));
-    LWIP_ERROR("mdns_build_service_domain: Failed to add label", (res == ERR_OK), return res);
-  }
-  res = mdns_domain_add_label(domain, service->service, (u8_t)strlen(service->service));
-  LWIP_ERROR("mdns_build_service_domain: Failed to add label", (res == ERR_OK), return res);
-  res = mdns_domain_add_label(domain, dnssd_protos[service->proto], (u8_t)strlen(dnssd_protos[service->proto]));
-  LWIP_ERROR("mdns_build_service_domain: Failed to add label", (res == ERR_OK), return res);
-  return mdns_add_dotlocal(domain);
-}
-
-/**
- * Check which replies we should send for a host/netif based on question
- * @param netif The network interface that received the question
- * @param rr Domain/type/class from a question
- * @param reverse_v6_reply Bitmask of which IPv6 addresses to send reverse PTRs for
- *                         if reply bit has REPLY_HOST_PTR_V6 set
- * @return Bitmask of which replies to send
- */
-static int
-check_host(struct netif *netif, struct mdns_rr_info *rr, u8_t *reverse_v6_reply)
-{
-  err_t res;
-  int replies = 0;
-  struct mdns_domain mydomain;
-
-  LWIP_UNUSED_ARG(reverse_v6_reply); /* if ipv6 is disabled */
-
-  if (rr->klass != DNS_RRCLASS_IN && rr->klass != DNS_RRCLASS_ANY) {
-    /* Invalid class */
-    return replies;
-  }
-
-  /* Handle PTR for our addresses */
-  if (rr->type == DNS_RRTYPE_PTR || rr->type == DNS_RRTYPE_ANY) {
-#if LWIP_IPV6
-    int i;
-    for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
-      if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i))) {
-        res = mdns_build_reverse_v6_domain(&mydomain, netif_ip6_addr(netif, i));
-        if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain)) {
-          replies |= REPLY_HOST_PTR_V6;
-          /* Mark which addresses where requested */
-          if (reverse_v6_reply) {
-            *reverse_v6_reply |= (1 << i);
-          }
-        }
-      }
-    }
-#endif
-#if LWIP_IPV4
-    if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) {
-      res = mdns_build_reverse_v4_domain(&mydomain, netif_ip4_addr(netif));
-      if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain)) {
-        replies |= REPLY_HOST_PTR_V4;
-      }
-    }
-#endif
-  }
-
-  res = mdns_build_host_domain(&mydomain, NETIF_TO_HOST(netif));
-  /* Handle requests for our hostname */
-  if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain)) {
-    /* TODO return NSEC if unsupported protocol requested */
-#if LWIP_IPV4
-    if (!ip4_addr_isany_val(*netif_ip4_addr(netif))
-        && (rr->type == DNS_RRTYPE_A || rr->type == DNS_RRTYPE_ANY)) {
-      replies |= REPLY_HOST_A;
-    }
-#endif
-#if LWIP_IPV6
-    if (rr->type == DNS_RRTYPE_AAAA || rr->type == DNS_RRTYPE_ANY) {
-      replies |= REPLY_HOST_AAAA;
-    }
-#endif
-  }
-
-  return replies;
-}
-
-/**
- * Check which replies we should send for a service based on question
- * @param service A registered MDNS service
- * @param rr Domain/type/class from a question
- * @return Bitmask of which replies to send
- */
-static int
-check_service(struct mdns_service *service, struct mdns_rr_info *rr)
-{
-  err_t res;
-  int replies = 0;
-  struct mdns_domain mydomain;
-
-  if (rr->klass != DNS_RRCLASS_IN && rr->klass != DNS_RRCLASS_ANY) {
-    /* Invalid class */
-    return 0;
-  }
-
-  res = mdns_build_dnssd_domain(&mydomain);
-  if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain) &&
-      (rr->type == DNS_RRTYPE_PTR || rr->type == DNS_RRTYPE_ANY)) {
-    /* Request for all service types */
-    replies |= REPLY_SERVICE_TYPE_PTR;
-  }
-
-  res = mdns_build_service_domain(&mydomain, service, 0);
-  if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain) &&
-      (rr->type == DNS_RRTYPE_PTR || rr->type == DNS_RRTYPE_ANY)) {
-    /* Request for the instance of my service */
-    replies |= REPLY_SERVICE_NAME_PTR;
-  }
-
-  res = mdns_build_service_domain(&mydomain, service, 1);
-  if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain)) {
-    /* Request for info about my service */
-    if (rr->type == DNS_RRTYPE_SRV || rr->type == DNS_RRTYPE_ANY) {
-      replies |= REPLY_SERVICE_SRV;
-    }
-    if (rr->type == DNS_RRTYPE_TXT || rr->type == DNS_RRTYPE_ANY) {
-      replies |= REPLY_SERVICE_TXT;
-    }
-  }
-
-  return replies;
-}
-
-/**
- * Return bytes needed to write before jump for best result of compressing supplied domain
- * against domain in outpacket starting at specified offset.
- * If a match is found, offset is updated to where to jump to
- * @param pbuf Pointer to pbuf with the partially constructed DNS packet
- * @param offset Start position of a domain written earlier. If this location is suitable
- *               for compression, the pointer is updated to where in the domain to jump to.
- * @param domain The domain to write
- * @return Number of bytes to write of the new domain before writing a jump to the offset.
- *         If compression can not be done against this previous domain name, the full new
- *         domain length is returned.
- */
-u16_t
-mdns_compress_domain(struct pbuf *pbuf, u16_t *offset, struct mdns_domain *domain)
-{
-  struct mdns_domain target;
-  u16_t target_end;
-  u8_t target_len;
-  u8_t writelen = 0;
-  u8_t *ptr;
-  if (pbuf == NULL) {
-    return domain->length;
-  }
-  target_end = mdns_readname(pbuf, *offset, &target);
-  if (target_end == MDNS_READNAME_ERROR) {
-    return domain->length;
-  }
-  target_len = (u8_t)(target_end - *offset);
-  ptr = domain->name;
-  while (writelen < domain->length) {
-    u8_t domainlen = (u8_t)(domain->length - writelen);
-    u8_t labellen;
-    if (domainlen <= target.length && domainlen > DOMAIN_JUMP_SIZE) {
-      /* Compare domains if target is long enough, and we have enough left of the domain */
-      u8_t targetpos = (u8_t)(target.length - domainlen);
-      if ((targetpos + DOMAIN_JUMP_SIZE) >= target_len) {
-        /* We are checking at or beyond a jump in the original, stop looking */
-        break;
-      }
-      if (target.length >= domainlen &&
-          memcmp(&domain->name[writelen], &target.name[targetpos], domainlen) == 0) {
-        *offset += targetpos;
-        return writelen;
-      }
-    }
-    /* Skip to next label in domain */
-    labellen = *ptr;
-    writelen += 1 + labellen;
-    ptr += 1 + labellen;
-  }
-  /* Nothing found */
-  return domain->length;
-}
-
-/**
- * Write domain to outpacket. Compression will be attempted,
- * unless domain->skip_compression is set.
- * @param outpkt The outpacket to write to
- * @param domain The domain name to write
- * @return ERR_OK on success, an err_t otherwise
- */
-static err_t
-mdns_write_domain(struct mdns_outpacket *outpkt, struct mdns_domain *domain)
-{
-  int i;
-  err_t res;
-  u16_t writelen = domain->length;
-  u16_t jump_offset = 0;
-  u16_t jump;
-
-  if (!domain->skip_compression) {
-    for (i = 0; i < NUM_DOMAIN_OFFSETS; i++) {
-      u16_t offset = outpkt->domain_offsets[i];
-      if (offset) {
-        u16_t len = mdns_compress_domain(outpkt->pbuf, &offset, domain);
-        if (len < writelen) {
-          writelen = len;
-          jump_offset = offset;
-        }
-      }
-    }
-  }
-
-  if (writelen) {
-    /* Write uncompressed part of name */
-    res = pbuf_take_at(outpkt->pbuf, domain->name, writelen, outpkt->write_offset);
-    if (res != ERR_OK) {
-      return res;
-    }
-
-    /* Store offset of this new domain */
-    for (i = 0; i < NUM_DOMAIN_OFFSETS; i++) {
-      if (outpkt->domain_offsets[i] == 0) {
-        outpkt->domain_offsets[i] = outpkt->write_offset;
-        break;
-      }
-    }
-
-    outpkt->write_offset += writelen;
-  }
-  if (jump_offset) {
-    /* Write jump */
-    jump = lwip_htons(DOMAIN_JUMP | jump_offset);
-    res = pbuf_take_at(outpkt->pbuf, &jump, DOMAIN_JUMP_SIZE, outpkt->write_offset);
-    if (res != ERR_OK) {
-      return res;
-    }
-    outpkt->write_offset += DOMAIN_JUMP_SIZE;
-  }
-  return ERR_OK;
-}
-
-/**
- * Write a question to an outpacket
- * A question contains domain, type and class. Since an answer also starts with these fields this function is also
- * called from mdns_add_answer().
- * @param outpkt The outpacket to write to
- * @param domain The domain name the answer is for
- * @param type The DNS type of the answer (like 'AAAA', 'SRV')
- * @param klass The DNS type of the answer (like 'IN')
- * @param unicast If highest bit in class should be set, to instruct the responder to
- *                reply with a unicast packet
- * @return ERR_OK on success, an err_t otherwise
- */
-static err_t
-mdns_add_question(struct mdns_outpacket *outpkt, struct mdns_domain *domain, u16_t type, u16_t klass, u16_t unicast)
-{
-  u16_t question_len;
-  u16_t field16;
-  err_t res;
-
-  if (!outpkt->pbuf) {
-    /* If no pbuf is active, allocate one */
-    outpkt->pbuf = pbuf_alloc(PBUF_TRANSPORT, OUTPACKET_SIZE, PBUF_RAM);
-    if (!outpkt->pbuf) {
-      return ERR_MEM;
-    }
-    outpkt->write_offset = SIZEOF_DNS_HDR;
-  }
-
-  /* Worst case calculation. Domain string might be compressed */
-  question_len = domain->length + sizeof(type) + sizeof(klass);
-  if (outpkt->write_offset + question_len > outpkt->pbuf->tot_len) {
-    /* No space */
-    return ERR_MEM;
-  }
-
-  /* Write name */
-  res = mdns_write_domain(outpkt, domain);
-  if (res != ERR_OK) {
-    return res;
-  }
-
-  /* Write type */
-  field16 = lwip_htons(type);
-  res = pbuf_take_at(outpkt->pbuf, &field16, sizeof(field16), outpkt->write_offset);
-  if (res != ERR_OK) {
-    return res;
-  }
-  outpkt->write_offset += sizeof(field16);
-
-  /* Write class */
-  if (unicast) {
-    klass |= 0x8000;
-  }
-  field16 = lwip_htons(klass);
-  res = pbuf_take_at(outpkt->pbuf, &field16, sizeof(field16), outpkt->write_offset);
-  if (res != ERR_OK) {
-    return res;
-  }
-  outpkt->write_offset += sizeof(field16);
-
-  return ERR_OK;
-}
-
-/**
- * Write answer to reply packet.
- * buf or answer_domain can be null. The rd_length written will be buf_length +
- * size of (compressed) domain. Most uses will need either buf or answer_domain,
- * special case is SRV that starts with 3 u16 and then a domain name.
- * @param reply The outpacket to write to
- * @param domain The domain name the answer is for
- * @param type The DNS type of the answer (like 'AAAA', 'SRV')
- * @param klass The DNS type of the answer (like 'IN')
- * @param cache_flush If highest bit in class should be set, to instruct receiver that
- *                    this reply replaces any earlier answer for this domain/type/class
- * @param ttl Validity time in seconds to send out for IP address data in DNS replies
- * @param buf Pointer to buffer of answer data
- * @param buf_length Length of variable data
- * @param answer_domain A domain to write after any buffer data as answer
- * @return ERR_OK on success, an err_t otherwise
- */
-static err_t
-mdns_add_answer(struct mdns_outpacket *reply, struct mdns_domain *domain, u16_t type, u16_t klass, u16_t cache_flush,
-                u32_t ttl, const u8_t *buf, size_t buf_length, struct mdns_domain *answer_domain)
-{
-  u16_t answer_len;
-  u16_t field16;
-  u16_t rdlen_offset;
-  u16_t answer_offset;
-  u32_t field32;
-  err_t res;
-
-  if (!reply->pbuf) {
-    /* If no pbuf is active, allocate one */
-    reply->pbuf = pbuf_alloc(PBUF_TRANSPORT, OUTPACKET_SIZE, PBUF_RAM);
-    if (!reply->pbuf) {
-      return ERR_MEM;
-    }
-    reply->write_offset = SIZEOF_DNS_HDR;
-  }
-
-  /* Worst case calculation. Domain strings might be compressed */
-  answer_len = domain->length + sizeof(type) + sizeof(klass) + sizeof(ttl) + sizeof(field16)/*rd_length*/;
-  if (buf) {
-    answer_len += (u16_t)buf_length;
-  }
-  if (answer_domain) {
-    answer_len += answer_domain->length;
-  }
-  if (reply->write_offset + answer_len > reply->pbuf->tot_len) {
-    /* No space */
-    return ERR_MEM;
-  }
-
-  /* Answer starts with same data as question, then more fields */
-  mdns_add_question(reply, domain, type, klass, cache_flush);
-
-  /* Write TTL */
-  field32 = lwip_htonl(ttl);
-  res = pbuf_take_at(reply->pbuf, &field32, sizeof(field32), reply->write_offset);
-  if (res != ERR_OK) {
-    return res;
-  }
-  reply->write_offset += sizeof(field32);
-
-  /* Store offsets and skip forward to the data */
-  rdlen_offset = reply->write_offset;
-  reply->write_offset += sizeof(field16);
-  answer_offset = reply->write_offset;
-
-  if (buf) {
-    /* Write static data */
-    res = pbuf_take_at(reply->pbuf, buf, (u16_t)buf_length, reply->write_offset);
-    if (res != ERR_OK) {
-      return res;
-    }
-    reply->write_offset += (u16_t)buf_length;
-  }
-
-  if (answer_domain) {
-    /* Write name answer (compressed if possible) */
-    res = mdns_write_domain(reply, answer_domain);
-    if (res != ERR_OK) {
-      return res;
-    }
-  }
-
-  /* Write rd_length after when we know the answer size */
-  field16 = lwip_htons(reply->write_offset - answer_offset);
-  res = pbuf_take_at(reply->pbuf, &field16, sizeof(field16), rdlen_offset);
-
-  return res;
-}
-
-/**
- * Helper function for mdns_read_question/mdns_read_answer
- * Reads a domain, type and class from the packet
- * @param pkt The MDNS packet to read from. The parse_offset field will be
- *            incremented to point to the next unparsed byte.
- * @param info The struct to fill with domain, type and class
- * @return ERR_OK on success, an err_t otherwise
- */
-static err_t
-mdns_read_rr_info(struct mdns_packet *pkt, struct mdns_rr_info *info)
-{
-  u16_t field16, copied;
-  pkt->parse_offset = mdns_readname(pkt->pbuf, pkt->parse_offset, &info->domain);
-  if (pkt->parse_offset == MDNS_READNAME_ERROR) {
-    return ERR_VAL;
-  }
-
-  copied = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), pkt->parse_offset);
-  if (copied != sizeof(field16)) {
-    return ERR_VAL;
-  }
-  pkt->parse_offset += copied;
-  info->type = lwip_ntohs(field16);
-
-  copied = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), pkt->parse_offset);
-  if (copied != sizeof(field16)) {
-    return ERR_VAL;
-  }
-  pkt->parse_offset += copied;
-  info->klass = lwip_ntohs(field16);
-
-  return ERR_OK;
-}
-
-/**
- * Read a question from the packet.
- * All questions have to be read before the answers.
- * @param pkt The MDNS packet to read from. The questions_left field will be decremented
- *            and the parse_offset will be updated.
- * @param question The struct to fill with question data
- * @return ERR_OK on success, an err_t otherwise
- */
-static err_t
-mdns_read_question(struct mdns_packet *pkt, struct mdns_question *question)
-{
-  /* Safety check */
-  if (pkt->pbuf->tot_len < pkt->parse_offset) {
-    return ERR_VAL;
-  }
-
-  if (pkt->questions_left) {
-    err_t res;
-    pkt->questions_left--;
-
-    memset(question, 0, sizeof(struct mdns_question));
-    res = mdns_read_rr_info(pkt, &question->info);
-    if (res != ERR_OK) {
-      return res;
-    }
-
-    /* Extract unicast flag from class field */
-    question->unicast = question->info.klass & 0x8000;
-    question->info.klass &= 0x7FFF;
-
-    return ERR_OK;
-  }
-  return ERR_VAL;
-}
-
-/**
- * Read an answer from the packet
- * The variable length reply is not copied, its pbuf offset and length is stored instead.
- * @param pkt The MDNS packet to read. The answers_left field will be decremented and
- *            the parse_offset will be updated.
- * @param answer The struct to fill with answer data
- * @return ERR_OK on success, an err_t otherwise
- */
-static err_t
-mdns_read_answer(struct mdns_packet *pkt, struct mdns_answer *answer)
-{
-  /* Read questions first */
-  if (pkt->questions_left) {
-    return ERR_VAL;
-  }
-
-  /* Safety check */
-  if (pkt->pbuf->tot_len < pkt->parse_offset) {
-    return ERR_VAL;
-  }
-
-  if (pkt->answers_left) {
-    u16_t copied, field16;
-    u32_t ttl;
-    err_t res;
-    pkt->answers_left--;
-
-    memset(answer, 0, sizeof(struct mdns_answer));
-    res = mdns_read_rr_info(pkt, &answer->info);
-    if (res != ERR_OK) {
-      return res;
-    }
-
-    /* Extract cache_flush flag from class field */
-    answer->cache_flush = answer->info.klass & 0x8000;
-    answer->info.klass &= 0x7FFF;
-
-    copied = pbuf_copy_partial(pkt->pbuf, &ttl, sizeof(ttl), pkt->parse_offset);
-    if (copied != sizeof(ttl)) {
-      return ERR_VAL;
-    }
-    pkt->parse_offset += copied;
-    answer->ttl = lwip_ntohl(ttl);
-
-    copied = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), pkt->parse_offset);
-    if (copied != sizeof(field16)) {
-      return ERR_VAL;
-    }
-    pkt->parse_offset += copied;
-    answer->rd_length = lwip_ntohs(field16);
-
-    answer->rd_offset = pkt->parse_offset;
-    pkt->parse_offset += answer->rd_length;
-
-    return ERR_OK;
-  }
-  return ERR_VAL;
-}
-
-#if LWIP_IPV4
-/** Write an IPv4 address (A) RR to outpacket */
-static err_t
-mdns_add_a_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct netif *netif)
-{
-  struct mdns_domain host;
-  mdns_build_host_domain(&host, NETIF_TO_HOST(netif));
-  LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with A record\n"));
-  return mdns_add_answer(reply, &host, DNS_RRTYPE_A, DNS_RRCLASS_IN, cache_flush, (NETIF_TO_HOST(netif))->dns_ttl, (const u8_t *) netif_ip4_addr(netif), sizeof(ip4_addr_t), NULL);
-}
-
-/** Write a 4.3.2.1.in-addr.arpa -> hostname.local PTR RR to outpacket */
-static err_t
-mdns_add_hostv4_ptr_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct netif *netif)
-{
-  struct mdns_domain host, revhost;
-  mdns_build_host_domain(&host, NETIF_TO_HOST(netif));
-  mdns_build_reverse_v4_domain(&revhost, netif_ip4_addr(netif));
-  LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with v4 PTR record\n"));
-  return mdns_add_answer(reply, &revhost, DNS_RRTYPE_PTR, DNS_RRCLASS_IN, cache_flush, (NETIF_TO_HOST(netif))->dns_ttl, NULL, 0, &host);
-}
-#endif
-
-#if LWIP_IPV6
-/** Write an IPv6 address (AAAA) RR to outpacket */
-static err_t
-mdns_add_aaaa_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct netif *netif, int addrindex)
-{
-  struct mdns_domain host;
-  mdns_build_host_domain(&host, NETIF_TO_HOST(netif));
-  LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with AAAA record\n"));
-  return mdns_add_answer(reply, &host, DNS_RRTYPE_AAAA, DNS_RRCLASS_IN, cache_flush, (NETIF_TO_HOST(netif))->dns_ttl, (const u8_t *) netif_ip6_addr(netif, addrindex), sizeof(ip6_addr_p_t), NULL);
-}
-
-/** Write a x.y.z.ip6.arpa -> hostname.local PTR RR to outpacket */
-static err_t
-mdns_add_hostv6_ptr_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct netif *netif, int addrindex)
-{
-  struct mdns_domain host, revhost;
-  mdns_build_host_domain(&host, NETIF_TO_HOST(netif));
-  mdns_build_reverse_v6_domain(&revhost, netif_ip6_addr(netif, addrindex));
-  LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with v6 PTR record\n"));
-  return mdns_add_answer(reply, &revhost, DNS_RRTYPE_PTR, DNS_RRCLASS_IN, cache_flush, (NETIF_TO_HOST(netif))->dns_ttl, NULL, 0, &host);
-}
-#endif
-
-/** Write an all-services -> servicetype PTR RR to outpacket */
-static err_t
-mdns_add_servicetype_ptr_answer(struct mdns_outpacket *reply, struct mdns_service *service)
-{
-  struct mdns_domain service_type, service_dnssd;
-  mdns_build_service_domain(&service_type, service, 0);
-  mdns_build_dnssd_domain(&service_dnssd);
-  LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with service type PTR record\n"));
-  return mdns_add_answer(reply, &service_dnssd, DNS_RRTYPE_PTR, DNS_RRCLASS_IN, 0, service->dns_ttl, NULL, 0, &service_type);
-}
-
-/** Write a servicetype -> servicename PTR RR to outpacket */
-static err_t
-mdns_add_servicename_ptr_answer(struct mdns_outpacket *reply, struct mdns_service *service)
-{
-  struct mdns_domain service_type, service_instance;
-  mdns_build_service_domain(&service_type, service, 0);
-  mdns_build_service_domain(&service_instance, service, 1);
-  LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with service name PTR record\n"));
-  return mdns_add_answer(reply, &service_type, DNS_RRTYPE_PTR, DNS_RRCLASS_IN, 0, service->dns_ttl, NULL, 0, &service_instance);
-}
-
-/** Write a SRV RR to outpacket */
-static err_t
-mdns_add_srv_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct mdns_host *mdns, struct mdns_service *service)
-{
-  struct mdns_domain service_instance, srvhost;
-  u16_t srvdata[3];
-  mdns_build_service_domain(&service_instance, service, 1);
-  mdns_build_host_domain(&srvhost, mdns);
-  if (reply->legacy_query) {
-    /* RFC 6762 section 18.14:
-     * In legacy unicast responses generated to answer legacy queries,
-     * name compression MUST NOT be performed on SRV records.
-     */
-    srvhost.skip_compression = 1;
-  }
-  srvdata[0] = lwip_htons(SRV_PRIORITY);
-  srvdata[1] = lwip_htons(SRV_WEIGHT);
-  srvdata[2] = lwip_htons(service->port);
-  LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with SRV record\n"));
-  return mdns_add_answer(reply, &service_instance, DNS_RRTYPE_SRV, DNS_RRCLASS_IN, cache_flush, service->dns_ttl,
-                         (const u8_t *) &srvdata, sizeof(srvdata), &srvhost);
-}
-
-/** Write a TXT RR to outpacket */
-static err_t
-mdns_add_txt_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct mdns_service *service)
-{
-  struct mdns_domain service_instance;
-  mdns_build_service_domain(&service_instance, service, 1);
-  mdns_prepare_txtdata(service);
-  LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with TXT record\n"));
-  return mdns_add_answer(reply, &service_instance, DNS_RRTYPE_TXT, DNS_RRCLASS_IN, cache_flush, service->dns_ttl,
-                         (u8_t *) &service->txtdata.name, service->txtdata.length, NULL);
-}
-
-/**
- * Setup outpacket as a reply to the incoming packet
- */
-static void
-mdns_init_outpacket(struct mdns_outpacket *out, struct mdns_packet *in)
-{
-  memset(out, 0, sizeof(struct mdns_outpacket));
-  out->cache_flush = 1;
-  out->netif = in->netif;
-
-  /* Copy source IP/port to use when responding unicast, or to choose
-   * which pcb to use for multicast (IPv4/IPv6)
-   */
-  SMEMCPY(&out->dest_addr, &in->source_addr, sizeof(ip_addr_t));
-  out->dest_port = in->source_port;
-
-  if (in->source_port != LWIP_IANA_PORT_MDNS) {
-    out->unicast_reply = 1;
-    out->cache_flush = 0;
-    if (in->questions == 1) {
-      out->legacy_query = 1;
-      out->tx_id = in->tx_id;
-    }
-  }
-
-  if (in->recv_unicast) {
-    out->unicast_reply = 1;
-  }
-}
-
-/**
- * Send chosen answers as a reply
- *
- * Add all selected answers (first write will allocate pbuf)
- * Add additional answers based on the selected answers
- * Send the packet
- */
-static err_t
-mdns_send_outpacket(struct mdns_outpacket *outpkt, u8_t flags)
-{
-  struct mdns_service *service;
-  err_t res = ERR_ARG;
-  int i;
-  struct mdns_host *mdns = NETIF_TO_HOST(outpkt->netif);
-  u16_t answers = 0;
-
-  /* Write answers to host questions */
-#if LWIP_IPV4
-  if (outpkt->host_replies & REPLY_HOST_A) {
-    res = mdns_add_a_answer(outpkt, outpkt->cache_flush, outpkt->netif);
-    if (res != ERR_OK) {
-      goto cleanup;
-    }
-    answers++;
-  }
-  if (outpkt->host_replies & REPLY_HOST_PTR_V4) {
-    res = mdns_add_hostv4_ptr_answer(outpkt, outpkt->cache_flush, outpkt->netif);
-    if (res != ERR_OK) {
-      goto cleanup;
-    }
-    answers++;
-  }
-#endif
-#if LWIP_IPV6
-  if (outpkt->host_replies & REPLY_HOST_AAAA) {
-    int addrindex;
-    for (addrindex = 0; addrindex < LWIP_IPV6_NUM_ADDRESSES; addrindex++) {
-      if (ip6_addr_isvalid(netif_ip6_addr_state(outpkt->netif, addrindex))) {
-        res = mdns_add_aaaa_answer(outpkt, outpkt->cache_flush, outpkt->netif, addrindex);
-        if (res != ERR_OK) {
-          goto cleanup;
-        }
-        answers++;
-      }
-    }
-  }
-  if (outpkt->host_replies & REPLY_HOST_PTR_V6) {
-    u8_t rev_addrs = outpkt->host_reverse_v6_replies;
-    int addrindex = 0;
-    while (rev_addrs) {
-      if (rev_addrs & 1) {
-        res = mdns_add_hostv6_ptr_answer(outpkt, outpkt->cache_flush, outpkt->netif, addrindex);
-        if (res != ERR_OK) {
-          goto cleanup;
-        }
-        answers++;
-      }
-      addrindex++;
-      rev_addrs >>= 1;
-    }
-  }
-#endif
-
-  /* Write answers to service questions */
-  for (i = 0; i < MDNS_MAX_SERVICES; i++) {
-    service = mdns->services[i];
-    if (!service) {
-      continue;
-    }
-
-    if (outpkt->serv_replies[i] & REPLY_SERVICE_TYPE_PTR) {
-      res = mdns_add_servicetype_ptr_answer(outpkt, service);
-      if (res != ERR_OK) {
-        goto cleanup;
-      }
-      answers++;
-    }
-
-    if (outpkt->serv_replies[i] & REPLY_SERVICE_NAME_PTR) {
-      res = mdns_add_servicename_ptr_answer(outpkt, service);
-      if (res != ERR_OK) {
-        goto cleanup;
-      }
-      answers++;
-    }
-
-    if (outpkt->serv_replies[i] & REPLY_SERVICE_SRV) {
-      res = mdns_add_srv_answer(outpkt, outpkt->cache_flush, mdns, service);
-      if (res != ERR_OK) {
-        goto cleanup;
-      }
-      answers++;
-    }
-
-    if (outpkt->serv_replies[i] & REPLY_SERVICE_TXT) {
-      res = mdns_add_txt_answer(outpkt, outpkt->cache_flush, service);
-      if (res != ERR_OK) {
-        goto cleanup;
-      }
-      answers++;
-    }
-  }
-
-  /* if this is a response, the data above is anwers, else this is a probe and the answers above goes into auth section */
-  if (flags & DNS_FLAG1_RESPONSE) {
-    outpkt->answers += answers;
-  } else {
-    outpkt->authoritative += answers;
-  }
-
-  /* All answers written, add additional RRs */
-  for (i = 0; i < MDNS_MAX_SERVICES; i++) {
-    service = mdns->services[i];
-    if (!service) {
-      continue;
-    }
-
-    if (outpkt->serv_replies[i] & REPLY_SERVICE_NAME_PTR) {
-      /* Our service instance requested, include SRV & TXT
-       * if they are already not requested. */
-      if (!(outpkt->serv_replies[i] & REPLY_SERVICE_SRV)) {
-        res = mdns_add_srv_answer(outpkt, outpkt->cache_flush, mdns, service);
-        if (res != ERR_OK) {
-          goto cleanup;
-        }
-        outpkt->additional++;
-      }
-
-      if (!(outpkt->serv_replies[i] & REPLY_SERVICE_TXT)) {
-        res = mdns_add_txt_answer(outpkt, outpkt->cache_flush, service);
-        if (res != ERR_OK) {
-          goto cleanup;
-        }
-        outpkt->additional++;
-      }
-    }
-
-    /* If service instance, SRV, record or an IP address is requested,
-     * supply all addresses for the host
-     */
-    if ((outpkt->serv_replies[i] & (REPLY_SERVICE_NAME_PTR | REPLY_SERVICE_SRV)) ||
-        (outpkt->host_replies & (REPLY_HOST_A | REPLY_HOST_AAAA))) {
-#if LWIP_IPV6
-      if (!(outpkt->host_replies & REPLY_HOST_AAAA)) {
-        int addrindex;
-        for (addrindex = 0; addrindex < LWIP_IPV6_NUM_ADDRESSES; addrindex++) {
-          if (ip6_addr_isvalid(netif_ip6_addr_state(outpkt->netif, addrindex))) {
-            res = mdns_add_aaaa_answer(outpkt, outpkt->cache_flush, outpkt->netif, addrindex);
-            if (res != ERR_OK) {
-              goto cleanup;
-            }
-            outpkt->additional++;
-          }
-        }
-      }
-#endif
-#if LWIP_IPV4
-      if (!(outpkt->host_replies & REPLY_HOST_A) &&
-          !ip4_addr_isany_val(*netif_ip4_addr(outpkt->netif))) {
-        res = mdns_add_a_answer(outpkt, outpkt->cache_flush, outpkt->netif);
-        if (res != ERR_OK) {
-          goto cleanup;
-        }
-        outpkt->additional++;
-      }
-#endif
-    }
-  }
-
-  if (outpkt->pbuf) {
-    const ip_addr_t *mcast_destaddr;
-    struct dns_hdr hdr;
-
-    /* Write header */
-    memset(&hdr, 0, sizeof(hdr));
-    hdr.flags1 = flags;
-    hdr.numquestions = lwip_htons(outpkt->questions);
-    hdr.numanswers = lwip_htons(outpkt->answers);
-    hdr.numauthrr = lwip_htons(outpkt->authoritative);
-    hdr.numextrarr = lwip_htons(outpkt->additional);
-    hdr.id = lwip_htons(outpkt->tx_id);
-    pbuf_take(outpkt->pbuf, &hdr, sizeof(hdr));
-
-    /* Shrink packet */
-    pbuf_realloc(outpkt->pbuf, outpkt->write_offset);
-
-    if (IP_IS_V6_VAL(outpkt->dest_addr)) {
-#if LWIP_IPV6
-      mcast_destaddr = &v6group;
-#endif
-    } else {
-#if LWIP_IPV4
-      mcast_destaddr = &v4group;
-#endif
-    }
-    /* Send created packet */
-    LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Sending packet, len=%d, unicast=%d\n", outpkt->write_offset, outpkt->unicast_reply));
-    if (outpkt->unicast_reply) {
-      res = udp_sendto_if(mdns_pcb, outpkt->pbuf, &outpkt->dest_addr, outpkt->dest_port, outpkt->netif);
-    } else {
-      res = udp_sendto_if(mdns_pcb, outpkt->pbuf, mcast_destaddr, LWIP_IANA_PORT_MDNS, outpkt->netif);
-    }
-  }
-
-cleanup:
-  if (outpkt->pbuf) {
-    pbuf_free(outpkt->pbuf);
-    outpkt->pbuf = NULL;
-  }
-  return res;
-}
-
-/**
- * Send unsolicited answer containing all our known data
- * @param netif The network interface to send on
- * @param destination The target address to send to (usually multicast address)
- */
-static void
-mdns_announce(struct netif *netif, const ip_addr_t *destination)
-{
-  struct mdns_outpacket announce;
-  int i;
-  struct mdns_host *mdns = NETIF_TO_HOST(netif);
-
-  memset(&announce, 0, sizeof(announce));
-  announce.netif = netif;
-  announce.cache_flush = 1;
-#if LWIP_IPV4
-  if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) {
-    announce.host_replies = REPLY_HOST_A | REPLY_HOST_PTR_V4;
-  }
-#endif
-#if LWIP_IPV6
-  for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
-    if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i))) {
-      announce.host_replies |= REPLY_HOST_AAAA | REPLY_HOST_PTR_V6;
-      announce.host_reverse_v6_replies |= (1 << i);
-    }
-  }
-#endif
-
-  for (i = 0; i < MDNS_MAX_SERVICES; i++) {
-    struct mdns_service *serv = mdns->services[i];
-    if (serv) {
-      announce.serv_replies[i] = REPLY_SERVICE_TYPE_PTR | REPLY_SERVICE_NAME_PTR |
-                                 REPLY_SERVICE_SRV | REPLY_SERVICE_TXT;
-    }
-  }
-
-  announce.dest_port = LWIP_IANA_PORT_MDNS;
-  SMEMCPY(&announce.dest_addr, destination, sizeof(announce.dest_addr));
-  mdns_send_outpacket(&announce, DNS_FLAG1_RESPONSE | DNS_FLAG1_AUTHORATIVE);
-}
-
-/**
- * Handle question MDNS packet
- * 1. Parse all questions and set bits what answers to send
- * 2. Clear pending answers if known answers are supplied
- * 3. Put chosen answers in new packet and send as reply
- */
-static void
-mdns_handle_question(struct mdns_packet *pkt)
-{
-  struct mdns_service *service;
-  struct mdns_outpacket reply;
-  int replies = 0;
-  int i;
-  err_t res;
-  struct mdns_host *mdns = NETIF_TO_HOST(pkt->netif);
-
-  if (mdns->probing_state != MDNS_PROBING_COMPLETE) {
-    /* Don't answer questions until we've verified our domains via probing */
-    /* @todo we should check incoming questions during probing for tiebreaking */
-    return;
-  }
-
-  mdns_init_outpacket(&reply, pkt);
-
-  while (pkt->questions_left) {
-    struct mdns_question q;
-
-    res = mdns_read_question(pkt, &q);
-    if (res != ERR_OK) {
-      LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Failed to parse question, skipping query packet\n"));
-      return;
-    }
-
-    LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Query for domain "));
-    mdns_domain_debug_print(&q.info.domain);
-    LWIP_DEBUGF(MDNS_DEBUG, (" type %d class %d\n", q.info.type, q.info.klass));
-
-    if (q.unicast) {
-      /* Reply unicast if any question is unicast */
-      reply.unicast_reply = 1;
-    }
-
-    reply.host_replies |= check_host(pkt->netif, &q.info, &reply.host_reverse_v6_replies);
-    replies |= reply.host_replies;
-
-    for (i = 0; i < MDNS_MAX_SERVICES; i++) {
-      service = mdns->services[i];
-      if (!service) {
-        continue;
-      }
-      reply.serv_replies[i] |= check_service(service, &q.info);
-      replies |= reply.serv_replies[i];
-    }
-
-    if (replies && reply.legacy_query) {
-      /* Add question to reply packet (legacy packet only has 1 question) */
-      res = mdns_add_question(&reply, &q.info.domain, q.info.type, q.info.klass, 0);
-      reply.questions = 1;
-      if (res != ERR_OK) {
-        goto cleanup;
-      }
-    }
-  }
-
-  /* Handle known answers */
-  while (pkt->answers_left) {
-    struct mdns_answer ans;
-    u8_t rev_v6;
-    int match;
-
-    res = mdns_read_answer(pkt, &ans);
-    if (res != ERR_OK) {
-      LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Failed to parse answer, skipping query packet\n"));
-      goto cleanup;
-    }
-
-    LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Known answer for domain "));
-    mdns_domain_debug_print(&ans.info.domain);
-    LWIP_DEBUGF(MDNS_DEBUG, (" type %d class %d\n", ans.info.type, ans.info.klass));
-
-
-    if (ans.info.type == DNS_RRTYPE_ANY || ans.info.klass == DNS_RRCLASS_ANY) {
-      /* Skip known answers for ANY type & class */
-      continue;
-    }
-
-    rev_v6 = 0;
-    match = reply.host_replies & check_host(pkt->netif, &ans.info, &rev_v6);
-    if (match && (ans.ttl > (mdns->dns_ttl / 2))) {
-      /* The RR in the known answer matches an RR we are planning to send,
-       * and the TTL is less than half gone.
-       * If the payload matches we should not send that answer.
-       */
-      if (ans.info.type == DNS_RRTYPE_PTR) {
-        /* Read domain and compare */
-        struct mdns_domain known_ans, my_ans;
-        u16_t len;
-        len = mdns_readname(pkt->pbuf, ans.rd_offset, &known_ans);
-        res = mdns_build_host_domain(&my_ans, mdns);
-        if (len != MDNS_READNAME_ERROR && res == ERR_OK && mdns_domain_eq(&known_ans, &my_ans)) {
-#if LWIP_IPV4
-          if (match & REPLY_HOST_PTR_V4) {
-            LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: v4 PTR\n"));
-            reply.host_replies &= ~REPLY_HOST_PTR_V4;
-          }
-#endif
-#if LWIP_IPV6
-          if (match & REPLY_HOST_PTR_V6) {
-            LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: v6 PTR\n"));
-            reply.host_reverse_v6_replies &= ~rev_v6;
-            if (reply.host_reverse_v6_replies == 0) {
-              reply.host_replies &= ~REPLY_HOST_PTR_V6;
-            }
-          }
-#endif
-        }
-      } else if (match & REPLY_HOST_A) {
-#if LWIP_IPV4
-        if (ans.rd_length == sizeof(ip4_addr_t) &&
-            pbuf_memcmp(pkt->pbuf, ans.rd_offset, netif_ip4_addr(pkt->netif), ans.rd_length) == 0) {
-          LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: A\n"));
-          reply.host_replies &= ~REPLY_HOST_A;
-        }
-#endif
-      } else if (match & REPLY_HOST_AAAA) {
-#if LWIP_IPV6
-        if (ans.rd_length == sizeof(ip6_addr_p_t) &&
-            /* TODO this clears all AAAA responses if first addr is set as known */
-            pbuf_memcmp(pkt->pbuf, ans.rd_offset, netif_ip6_addr(pkt->netif, 0), ans.rd_length) == 0) {
-          LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: AAAA\n"));
-          reply.host_replies &= ~REPLY_HOST_AAAA;
-        }
-#endif
-      }
-    }
-
-    for (i = 0; i < MDNS_MAX_SERVICES; i++) {
-      service = mdns->services[i];
-      if (!service) {
-        continue;
-      }
-      match = reply.serv_replies[i] & check_service(service, &ans.info);
-      if (match && (ans.ttl > (service->dns_ttl / 2))) {
-        /* The RR in the known answer matches an RR we are planning to send,
-         * and the TTL is less than half gone.
-         * If the payload matches we should not send that answer.
-         */
-        if (ans.info.type == DNS_RRTYPE_PTR) {
-          /* Read domain and compare */
-          struct mdns_domain known_ans, my_ans;
-          u16_t len;
-          len = mdns_readname(pkt->pbuf, ans.rd_offset, &known_ans);
-          if (len != MDNS_READNAME_ERROR) {
-            if (match & REPLY_SERVICE_TYPE_PTR) {
-              res = mdns_build_service_domain(&my_ans, service, 0);
-              if (res == ERR_OK && mdns_domain_eq(&known_ans, &my_ans)) {
-                LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: service type PTR\n"));
-                reply.serv_replies[i] &= ~REPLY_SERVICE_TYPE_PTR;
-              }
-            }
-            if (match & REPLY_SERVICE_NAME_PTR) {
-              res = mdns_build_service_domain(&my_ans, service, 1);
-              if (res == ERR_OK && mdns_domain_eq(&known_ans, &my_ans)) {
-                LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: service name PTR\n"));
-                reply.serv_replies[i] &= ~REPLY_SERVICE_NAME_PTR;
-              }
-            }
-          }
-        } else if (match & REPLY_SERVICE_SRV) {
-          /* Read and compare to my SRV record */
-          u16_t field16, len, read_pos;
-          struct mdns_domain known_ans, my_ans;
-          read_pos = ans.rd_offset;
-          do {
-            /* Check priority field */
-            len = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), read_pos);
-            if (len != sizeof(field16) || lwip_ntohs(field16) != SRV_PRIORITY) {
-              break;
-            }
-            read_pos += len;
-            /* Check weight field */
-            len = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), read_pos);
-            if (len != sizeof(field16) || lwip_ntohs(field16) != SRV_WEIGHT) {
-              break;
-            }
-            read_pos += len;
-            /* Check port field */
-            len = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), read_pos);
-            if (len != sizeof(field16) || lwip_ntohs(field16) != service->port) {
-              break;
-            }
-            read_pos += len;
-            /* Check host field */
-            len = mdns_readname(pkt->pbuf, read_pos, &known_ans);
-            mdns_build_host_domain(&my_ans, mdns);
-            if (len == MDNS_READNAME_ERROR || !mdns_domain_eq(&known_ans, &my_ans)) {
-              break;
-            }
-            LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: SRV\n"));
-            reply.serv_replies[i] &= ~REPLY_SERVICE_SRV;
-          } while (0);
-        } else if (match & REPLY_SERVICE_TXT) {
-          mdns_prepare_txtdata(service);
-          if (service->txtdata.length == ans.rd_length &&
-              pbuf_memcmp(pkt->pbuf, ans.rd_offset, service->txtdata.name, ans.rd_length) == 0) {
-            LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: TXT\n"));
-            reply.serv_replies[i] &= ~REPLY_SERVICE_TXT;
-          }
-        }
-      }
-    }
-  }
-
-  mdns_send_outpacket(&reply, DNS_FLAG1_RESPONSE | DNS_FLAG1_AUTHORATIVE);
-
-cleanup:
-  if (reply.pbuf) {
-    /* This should only happen if we fail to alloc/write question for legacy query */
-    pbuf_free(reply.pbuf);
-    reply.pbuf = NULL;
-  }
-}
-
-/**
- * Handle response MDNS packet
- * Only prints debug for now. Will need more code to do conflict resolution.
- */
-static void
-mdns_handle_response(struct mdns_packet *pkt)
-{
-  struct mdns_host* mdns = NETIF_TO_HOST(pkt->netif);
-
-  /* Ignore all questions */
-  while (pkt->questions_left) {
-    struct mdns_question q;
-    err_t res;
-
-    res = mdns_read_question(pkt, &q);
-    if (res != ERR_OK) {
-      LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Failed to parse question, skipping response packet\n"));
-      return;
-    }
-  }
-
-  while (pkt->answers_left) {
-    struct mdns_answer ans;
-    err_t res;
-
-    res = mdns_read_answer(pkt, &ans);
-    if (res != ERR_OK) {
-      LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Failed to parse answer, skipping response packet\n"));
-      return;
-    }
-
-    LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Answer for domain "));
-    mdns_domain_debug_print(&ans.info.domain);
-    LWIP_DEBUGF(MDNS_DEBUG, (" type %d class %d\n", ans.info.type, ans.info.klass));
-
-    /*"Apparently conflicting Multicast DNS responses received *before* the first probe packet is sent MUST
-      be silently ignored" so drop answer if we haven't started probing yet*/
-    if ((mdns->probing_state == MDNS_PROBING_ONGOING) && (mdns->probes_sent > 0)) {
-      struct mdns_domain domain;
-      u8_t i;
-      u8_t conflict = 0;
-
-      res = mdns_build_host_domain(&domain, mdns);
-      if (res == ERR_OK && mdns_domain_eq(&ans.info.domain, &domain)) {
-        LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Probe response matches host domain!"));
-        conflict = 1;
-      }
-
-      for (i = 0; i < MDNS_MAX_SERVICES; i++) {
-        struct mdns_service* service = mdns->services[i];
-        if (!service) {
-          continue;
-        }
-        res = mdns_build_service_domain(&domain, service, 1);
-        if ((res == ERR_OK) && mdns_domain_eq(&ans.info.domain, &domain)) {
-          LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Probe response matches service domain!"));
-          conflict = 1;
-        }
-      }
-
-      if (conflict != 0) {
-        sys_untimeout(mdns_probe, pkt->netif);
-        if (mdns_name_result_cb != NULL) {
-          mdns_name_result_cb(pkt->netif, MDNS_PROBING_CONFLICT);
-        }
-      }
-    }
-  }
-}
-
-/**
- * Receive input function for MDNS packets.
- * Handles both IPv4 and IPv6 UDP pcbs.
- */
-static void
-mdns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
-{
-  struct dns_hdr hdr;
-  struct mdns_packet packet;
-  struct netif *recv_netif = ip_current_input_netif();
-  u16_t offset = 0;
-
-  LWIP_UNUSED_ARG(arg);
-  LWIP_UNUSED_ARG(pcb);
-
-  LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Received IPv%d MDNS packet, len %d\n", IP_IS_V6(addr) ? 6 : 4, p->tot_len));
-
-  if (NETIF_TO_HOST(recv_netif) == NULL) {
-    /* From netif not configured for MDNS */
-    goto dealloc;
-  }
-
-  if (pbuf_copy_partial(p, &hdr, SIZEOF_DNS_HDR, offset) < SIZEOF_DNS_HDR) {
-    /* Too small */
-    goto dealloc;
-  }
-  offset += SIZEOF_DNS_HDR;
-
-  if (DNS_HDR_GET_OPCODE(&hdr)) {
-    /* Ignore non-standard queries in multicast packets (RFC 6762, section 18.3) */
-    goto dealloc;
-  }
-
-  memset(&packet, 0, sizeof(packet));
-  SMEMCPY(&packet.source_addr, addr, sizeof(packet.source_addr));
-  packet.source_port = port;
-  packet.netif = recv_netif;
-  packet.pbuf = p;
-  packet.parse_offset = offset;
-  packet.tx_id = lwip_ntohs(hdr.id);
-  packet.questions = packet.questions_left = lwip_ntohs(hdr.numquestions);
-  packet.answers = packet.answers_left = lwip_ntohs(hdr.numanswers) + lwip_ntohs(hdr.numauthrr) + lwip_ntohs(hdr.numextrarr);
-
-#if LWIP_IPV6
-  if (IP_IS_V6(ip_current_dest_addr())) {
-    /* instead of having one 'v6group' per netif, just compare zoneless here */
-    if (!ip_addr_cmp_zoneless(ip_current_dest_addr(), &v6group)) {
-      packet.recv_unicast = 1;
-    }
-  }
-#endif
-#if LWIP_IPV4
-  if (!IP_IS_V6(ip_current_dest_addr())) {
-    if (!ip_addr_cmp(ip_current_dest_addr(), &v4group)) {
-      packet.recv_unicast = 1;
-    }
-  }
-#endif
-
-  if (hdr.flags1 & DNS_FLAG1_RESPONSE) {
-    mdns_handle_response(&packet);
-  } else {
-    mdns_handle_question(&packet);
-  }
-
-dealloc:
-  pbuf_free(p);
-}
-
-#if LWIP_NETIF_EXT_STATUS_CALLBACK && MDNS_RESP_USENETIF_EXTCALLBACK
-static void
-mdns_netif_ext_status_callback(struct netif *netif, netif_nsc_reason_t reason, const netif_ext_callback_args_t *args)
-{
-  LWIP_UNUSED_ARG(args);
-
-  /* MDNS enabled on netif? */
-  if (NETIF_TO_HOST(netif) == NULL) {
-    return;
-  }
-
-  if (reason & LWIP_NSC_STATUS_CHANGED) {
-    if (args->status_changed.state != 0) {
-      mdns_resp_restart(netif);
-    }
-    /* TODO: send goodbye message */
-  }
-  if (reason & LWIP_NSC_LINK_CHANGED) {
-    if (args->link_changed.state != 0) {
-      mdns_resp_restart(netif);
-    }
-  }
-  if (reason & (LWIP_NSC_IPV4_ADDRESS_CHANGED | LWIP_NSC_IPV4_GATEWAY_CHANGED |
-      LWIP_NSC_IPV4_NETMASK_CHANGED | LWIP_NSC_IPV4_SETTINGS_CHANGED |
-      LWIP_NSC_IPV6_SET | LWIP_NSC_IPV6_ADDR_STATE_CHANGED)) {
-    mdns_resp_announce(netif);
-  }
-}
-#endif /* LWIP_NETIF_EXT_STATUS_CALLBACK && MDNS_RESP_USENETIF_EXTCALLBACK */
-
-static err_t
-mdns_send_probe(struct netif* netif, const ip_addr_t *destination)
-{
-  struct mdns_host* mdns;
-  struct mdns_outpacket pkt;
-  struct mdns_domain domain;
-  u8_t i;
-  err_t res;
-
-  mdns = NETIF_TO_HOST(netif);
-
-  memset(&pkt, 0, sizeof(pkt));
-  pkt.netif = netif;
-
-  /* Add unicast questions with rtype ANY for all our desired records */
-  mdns_build_host_domain(&domain, mdns);
-  res = mdns_add_question(&pkt, &domain, DNS_RRTYPE_ANY, DNS_RRCLASS_IN, 1);
-  if (res != ERR_OK) {
-    goto cleanup;
-  }
-  pkt.questions++;
-  for (i = 0; i < MDNS_MAX_SERVICES; i++) {
-    struct mdns_service* service = mdns->services[i];
-    if (!service) {
-      continue;
-    }
-    mdns_build_service_domain(&domain, service, 1);
-    res = mdns_add_question(&pkt, &domain, DNS_RRTYPE_ANY, DNS_RRCLASS_IN, 1);
-    if (res != ERR_OK) {
-      goto cleanup;
-    }
-    pkt.questions++;
-  }
-
-  /* Add answers to the questions above into the authority section for tiebreaking */
-#if LWIP_IPV4
-  if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) {
-    pkt.host_replies = REPLY_HOST_A;
-  }
-#endif
-#if LWIP_IPV6
-  for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
-    if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i))) {
-      pkt.host_replies |= REPLY_HOST_AAAA;
-    }
-  }
-#endif
-
-  for (i = 0; i < MDNS_MAX_SERVICES; i++) {
-    struct mdns_service *serv = mdns->services[i];
-    if (serv) {
-      pkt.serv_replies[i] = REPLY_SERVICE_SRV | REPLY_SERVICE_TXT;
-    }
-  }
-
-  pkt.tx_id = 0;
-  pkt.dest_port = LWIP_IANA_PORT_MDNS;
-  SMEMCPY(&pkt.dest_addr, destination, sizeof(pkt.dest_addr));
-  res = mdns_send_outpacket(&pkt, 0);
-
-cleanup:
-  if (pkt.pbuf) {
-    pbuf_free(pkt.pbuf);
-    pkt.pbuf = NULL;
-  }
-  return res;
-}
-
-/**
- * Timer callback for probing network.
- */
-static void
-mdns_probe(void* arg)
-{
-  struct netif *netif = (struct netif *)arg;
-  struct mdns_host* mdns = NETIF_TO_HOST(netif);
-
-  if(mdns->probes_sent >= MDNS_PROBE_COUNT) {
-    /* probing successful, announce the new name */
-    mdns->probing_state = MDNS_PROBING_COMPLETE;
-    mdns_resp_announce(netif);
-    if (mdns_name_result_cb != NULL) {
-      mdns_name_result_cb(netif, MDNS_PROBING_SUCCESSFUL);
-    }
-  } else {
-#if LWIP_IPV4
-    /*if ipv4 wait with probing until address is set*/
-    if (!ip4_addr_isany_val(*netif_ip4_addr(netif)) &&
-        mdns_send_probe(netif, IP4_ADDR_ANY) == ERR_OK)
-#endif
-    {
-#if LWIP_IPV6
-      if (mdns_send_probe(netif, IP6_ADDR_ANY) == ERR_OK)
-#endif
-      {
-        mdns->probes_sent++;
-      }
-    }
-    sys_timeout(MDNS_PROBE_DELAY_MS, mdns_probe, netif);
-  }
-}
-
-/**
- * @ingroup mdns
- * Activate MDNS responder for a network interface.
- * @param netif The network interface to activate.
- * @param hostname Name to use. Queries for &lt;hostname&gt;.local will be answered
- *                 with the IP addresses of the netif. The hostname will be copied, the
- *                 given pointer can be on the stack.
- * @param dns_ttl Validity time in seconds to send out for IP address data in DNS replies
- * @return ERR_OK if netif was added, an err_t otherwise
- */
-err_t
-mdns_resp_add_netif(struct netif *netif, const char *hostname, u32_t dns_ttl)
-{
-  err_t res;
-  struct mdns_host *mdns;
-
-  LWIP_ASSERT_CORE_LOCKED();
-  LWIP_ERROR("mdns_resp_add_netif: netif != NULL", (netif != NULL), return ERR_VAL);
-  LWIP_ERROR("mdns_resp_add_netif: Hostname too long", (strlen(hostname) <= MDNS_LABEL_MAXLEN), return ERR_VAL);
-
-  LWIP_ASSERT("mdns_resp_add_netif: Double add", NETIF_TO_HOST(netif) == NULL);
-  mdns = (struct mdns_host *) mem_calloc(1, sizeof(struct mdns_host));
-  LWIP_ERROR("mdns_resp_add_netif: Alloc failed", (mdns != NULL), return ERR_MEM);
-
-  netif_set_client_data(netif, mdns_netif_client_id, mdns);
-
-  MEMCPY(&mdns->name, hostname, LWIP_MIN(MDNS_LABEL_MAXLEN, strlen(hostname)));
-  mdns->dns_ttl = dns_ttl;
-  mdns->probes_sent = 0;
-  mdns->probing_state = MDNS_PROBING_NOT_STARTED;
-
-  /* Join multicast groups */
-#if LWIP_IPV4
-  res = igmp_joingroup_netif(netif, ip_2_ip4(&v4group));
-  if (res != ERR_OK) {
-    goto cleanup;
-  }
-#endif
-#if LWIP_IPV6
-  res = mld6_joingroup_netif(netif, ip_2_ip6(&v6group));
-  if (res != ERR_OK) {
-    goto cleanup;
-  }
-#endif
-
-  mdns_resp_restart(netif);
-
-  return ERR_OK;
-
-cleanup:
-  mem_free(mdns);
-  netif_set_client_data(netif, mdns_netif_client_id, NULL);
-  return res;
-}
-
-/**
- * @ingroup mdns
- * Stop responding to MDNS queries on this interface, leave multicast groups,
- * and free the helper structure and any of its services.
- * @param netif The network interface to remove.
- * @return ERR_OK if netif was removed, an err_t otherwise
- */
-err_t
-mdns_resp_remove_netif(struct netif *netif)
-{
-  int i;
-  struct mdns_host *mdns;
-
-  LWIP_ASSERT_CORE_LOCKED();
-  LWIP_ASSERT("mdns_resp_remove_netif: Null pointer", netif);
-  mdns = NETIF_TO_HOST(netif);
-  LWIP_ERROR("mdns_resp_remove_netif: Not an active netif", (mdns != NULL), return ERR_VAL);
-
-  if (mdns->probing_state == MDNS_PROBING_ONGOING) {
-    sys_untimeout(mdns_probe, netif);
-  }
-
-  for (i = 0; i < MDNS_MAX_SERVICES; i++) {
-    struct mdns_service *service = mdns->services[i];
-    if (service) {
-      mem_free(service);
-    }
-  }
-
-  /* Leave multicast groups */
-#if LWIP_IPV4
-  igmp_leavegroup_netif(netif, ip_2_ip4(&v4group));
-#endif
-#if LWIP_IPV6
-  mld6_leavegroup_netif(netif, ip_2_ip6(&v6group));
-#endif
-
-  mem_free(mdns);
-  netif_set_client_data(netif, mdns_netif_client_id, NULL);
-  return ERR_OK;
-}
-
-/**
- * @ingroup mdns
- * Update MDNS hostname for a network interface.
- * @param netif The network interface to activate.
- * @param hostname Name to use. Queries for &lt;hostname&gt;.local will be answered
- *                 with the IP addresses of the netif. The hostname will be copied, the
- *                 given pointer can be on the stack.
- * @return ERR_OK if name could be set on netif, an err_t otherwise
- */
-err_t
-mdns_resp_rename_netif(struct netif *netif, const char *hostname)
-{
-  struct mdns_host *mdns;
-  size_t len;
-
-  LWIP_ASSERT_CORE_LOCKED();
-  len = strlen(hostname);
-  LWIP_ERROR("mdns_resp_rename_netif: netif != NULL", (netif != NULL), return ERR_VAL);
-  LWIP_ERROR("mdns_resp_rename_netif: Hostname too long", (len <= MDNS_LABEL_MAXLEN), return ERR_VAL);
-  mdns = NETIF_TO_HOST(netif);
-  LWIP_ERROR("mdns_resp_rename_netif: Not an mdns netif", (mdns != NULL), return ERR_VAL);
-
-  MEMCPY(&mdns->name, hostname, LWIP_MIN(MDNS_LABEL_MAXLEN, len));
-  mdns->name[len] = '\0'; /* null termination in case new name is shorter than previous */
-
-  mdns_resp_restart(netif);
-
-  return ERR_OK;
-}
-
-/**
- * @ingroup mdns
- * Add a service to the selected network interface.
- * @param netif The network interface to publish this service on
- * @param name The name of the service
- * @param service The service type, like "_http"
- * @param proto The service protocol, DNSSD_PROTO_TCP for TCP ("_tcp") and DNSSD_PROTO_UDP
- *              for others ("_udp")
- * @param port The port the service listens to
- * @param dns_ttl Validity time in seconds to send out for service data in DNS replies
- * @param txt_fn Callback to get TXT data. Will be called each time a TXT reply is created to
- *               allow dynamic replies.
- * @param txt_data Userdata pointer for txt_fn
- * @return service_id if the service was added to the netif, an err_t otherwise
- */
-s8_t
-mdns_resp_add_service(struct netif *netif, const char *name, const char *service, enum mdns_sd_proto proto, u16_t port, u32_t dns_ttl, service_get_txt_fn_t txt_fn, void *txt_data)
-{
-  s8_t i;
-  s8_t slot = -1;
-  struct mdns_service *srv;
-  struct mdns_host *mdns;
-
-  LWIP_ASSERT_CORE_LOCKED();
-  LWIP_ASSERT("mdns_resp_add_service: netif != NULL", netif);
-  mdns = NETIF_TO_HOST(netif);
-  LWIP_ERROR("mdns_resp_add_service: Not an mdns netif", (mdns != NULL), return ERR_VAL);
-
-  LWIP_ERROR("mdns_resp_add_service: Name too long", (strlen(name) <= MDNS_LABEL_MAXLEN), return ERR_VAL);
-  LWIP_ERROR("mdns_resp_add_service: Service too long", (strlen(service) <= MDNS_LABEL_MAXLEN), return ERR_VAL);
-  LWIP_ERROR("mdns_resp_add_service: Bad proto (need TCP or UDP)", (proto == DNSSD_PROTO_TCP || proto == DNSSD_PROTO_UDP), return ERR_VAL);
-
-  for (i = 0; i < MDNS_MAX_SERVICES; i++) {
-    if (mdns->services[i] == NULL) {
-      slot = i;
-      break;
-    }
-  }
-  LWIP_ERROR("mdns_resp_add_service: Service list full (increase MDNS_MAX_SERVICES)", (slot >= 0), return ERR_MEM);
-
-  srv = (struct mdns_service *)mem_calloc(1, sizeof(struct mdns_service));
-  LWIP_ERROR("mdns_resp_add_service: Alloc failed", (srv != NULL), return ERR_MEM);
-
-  MEMCPY(&srv->name, name, LWIP_MIN(MDNS_LABEL_MAXLEN, strlen(name)));
-  MEMCPY(&srv->service, service, LWIP_MIN(MDNS_LABEL_MAXLEN, strlen(service)));
-  srv->txt_fn = txt_fn;
-  srv->txt_userdata = txt_data;
-  srv->proto = (u16_t)proto;
-  srv->port = port;
-  srv->dns_ttl = dns_ttl;
-
-  mdns->services[slot] = srv;
-
-  mdns_resp_restart(netif);
-
-  return slot;
-}
-
-/**
- * @ingroup mdns
- * Delete a service on the selected network interface.
- * @param netif The network interface on which service should be removed
- * @param slot The service slot number returned by mdns_resp_add_service
- * @return ERR_OK if the service was removed from the netif, an err_t otherwise
- */
-err_t
-mdns_resp_del_service(struct netif *netif, s8_t slot)
-{
-  struct mdns_host *mdns;
-  struct mdns_service *srv;
-  LWIP_ASSERT("mdns_resp_del_service: netif != NULL", netif);
-  mdns = NETIF_TO_HOST(netif);
-  LWIP_ERROR("mdns_resp_del_service: Not an mdns netif", (mdns != NULL), return ERR_VAL);
-  LWIP_ERROR("mdns_resp_del_service: Invalid Service ID", (slot >= 0) && (slot < MDNS_MAX_SERVICES), return ERR_VAL);
-  LWIP_ERROR("mdns_resp_del_service: Invalid Service ID", (mdns->services[slot] != NULL), return ERR_VAL);
-
-  srv = mdns->services[slot];
-  mdns->services[slot] = NULL;
-  mem_free(srv);
-  return ERR_OK;
-}
-
-/**
- * @ingroup mdns
- * Update name for an MDNS service.
- * @param netif The network interface to activate.
- * @param slot The service slot number returned by mdns_resp_add_service
- * @param name The new name for the service
- * @return ERR_OK if name could be set on service, an err_t otherwise
- */
-err_t
-mdns_resp_rename_service(struct netif *netif, s8_t slot, const char *name)
-{
-  struct mdns_service *srv;
-  struct mdns_host *mdns;
-  size_t len;
-
-  LWIP_ASSERT_CORE_LOCKED();
-  len = strlen(name);
-  LWIP_ASSERT("mdns_resp_rename_service: netif != NULL", netif);
-  mdns = NETIF_TO_HOST(netif);
-  LWIP_ERROR("mdns_resp_rename_service: Not an mdns netif", (mdns != NULL), return ERR_VAL);
-  LWIP_ERROR("mdns_resp_rename_service: Name too long", (len <= MDNS_LABEL_MAXLEN), return ERR_VAL);
-  LWIP_ERROR("mdns_resp_rename_service: Invalid Service ID", (slot >= 0) && (slot < MDNS_MAX_SERVICES), return ERR_VAL);
-  LWIP_ERROR("mdns_resp_rename_service: Invalid Service ID", (mdns->services[slot] != NULL), return ERR_VAL);
-
-  srv = mdns->services[slot];
-
-  MEMCPY(&srv->name, name, LWIP_MIN(MDNS_LABEL_MAXLEN, len));
-  srv->name[len] = '\0'; /* null termination in case new name is shorter than previous */
-
-  mdns_resp_restart(netif);
-
-  return ERR_OK;
-}
-
-/**
- * @ingroup mdns
- * Call this function from inside the service_get_txt_fn_t callback to add text data.
- * Buffer for TXT data is 256 bytes, and each field is prefixed with a length byte.
- * @param service The service provided to the get_txt callback
- * @param txt String to add to the TXT field.
- * @param txt_len Length of string
- * @return ERR_OK if the string was added to the reply, an err_t otherwise
- */
-err_t
-mdns_resp_add_service_txtitem(struct mdns_service *service, const char *txt, u8_t txt_len)
-{
-  LWIP_ASSERT_CORE_LOCKED();
-  LWIP_ASSERT("mdns_resp_add_service_txtitem: service != NULL", service);
-
-  /* Use a mdns_domain struct to store txt chunks since it is the same encoding */
-  return mdns_domain_add_label(&service->txtdata, txt, txt_len);
-}
-
-/**
- * @ingroup mdns
- * Send unsolicited answer containing all our known data
- * @param netif The network interface to send on
- */
-void
-mdns_resp_announce(struct netif *netif)
-{
-  struct mdns_host* mdns;
-  LWIP_ASSERT_CORE_LOCKED();
-  LWIP_ERROR("mdns_resp_announce: netif != NULL", (netif != NULL), return);
-
-  mdns = NETIF_TO_HOST(netif);
-  if (mdns == NULL) {
-    return;
-  }
-
-  if (mdns->probing_state == MDNS_PROBING_COMPLETE) {
-    /* Announce on IPv6 and IPv4 */
-#if LWIP_IPV6
-    mdns_announce(netif, IP6_ADDR_ANY);
-#endif
-#if LWIP_IPV4
-    if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) {
-      mdns_announce(netif, IP4_ADDR_ANY);
-    }
-#endif
-  } /* else: ip address changed while probing was ongoing? @todo reset counter to restart? */
-}
-
-/** Register a callback function that is called if probing is completed successfully
- * or with a conflict. */
-void
-mdns_resp_register_name_result_cb(mdns_name_result_cb_t cb)
-{
-  mdns_name_result_cb = cb;
-}
-
-/**
- * @ingroup mdns
- * Restart mdns responder. Call this when cable is connected after being disconnected or
- * administrative interface is set up after being down
- * @param netif The network interface to send on
- */
-void
-mdns_resp_restart(struct netif *netif)
-{
-  struct mdns_host* mdns;
-  LWIP_ASSERT_CORE_LOCKED();
-  LWIP_ERROR("mdns_resp_restart: netif != NULL", (netif != NULL), return);
-
-  mdns = NETIF_TO_HOST(netif);
-  if (mdns == NULL) {
-    return;
-  }
-
-  if (mdns->probing_state == MDNS_PROBING_ONGOING) {
-    sys_untimeout(mdns_probe, netif);
-  }
-  /* @todo if we've failed 15 times within a 10 second period we MUST wait 5 seconds (or wait 5 seconds every time except first)*/
-  mdns->probes_sent = 0;
-  mdns->probing_state = MDNS_PROBING_ONGOING;
-  sys_timeout(MDNS_INITIAL_PROBE_DELAY_MS, mdns_probe, netif);
-}
-
-/**
- * @ingroup mdns
- * Initiate MDNS responder. Will open UDP sockets on port 5353
- */
-void
-mdns_resp_init(void)
-{
-  err_t res;
-
-  /* LWIP_ASSERT_CORE_LOCKED(); is checked by udp_new() */
-
-  mdns_pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
-  LWIP_ASSERT("Failed to allocate pcb", mdns_pcb != NULL);
-#if LWIP_MULTICAST_TX_OPTIONS
-  udp_set_multicast_ttl(mdns_pcb, MDNS_TTL);
-#else
-  mdns_pcb->ttl = MDNS_TTL;
-#endif
-  res = udp_bind(mdns_pcb, IP_ANY_TYPE, LWIP_IANA_PORT_MDNS);
-  LWIP_UNUSED_ARG(res); /* in case of LWIP_NOASSERT */
-  LWIP_ASSERT("Failed to bind pcb", res == ERR_OK);
-  udp_recv(mdns_pcb, mdns_recv, NULL);
-
-  mdns_netif_client_id = netif_alloc_client_data_id();
-
-#if MDNS_RESP_USENETIF_EXTCALLBACK
-  /* register for netif events when started on first netif */
-  netif_add_ext_callback(&netif_callback, mdns_netif_ext_status_callback);
-#endif
-}
-
-#endif /* LWIP_MDNS_RESPONDER */
+/**
+ * @file
+ * MDNS responder implementation
+ *
+ * @defgroup mdns MDNS
+ * @ingroup apps
+ *
+ * RFC 6762 - Multicast DNS\n
+ * RFC 6763 - DNS-Based Service Discovery\n
+ * 
+ * @verbinclude mdns.txt
+ * 
+ * Things left to implement:
+ * -------------------------
+ *
+ * - Probing/conflict resolution
+ * - Sending goodbye messages (zero ttl) - shutdown, DHCP lease about to expire, DHCP turned off...
+ * - Checking that source address of unicast requests are on the same network
+ * - Limiting multicast responses to 1 per second per resource record
+ * - Fragmenting replies if required
+ * - Subscribe to netif address/link change events and act on them (currently needs to be done manually)
+ * - Handling multi-packet known answers
+ * - Individual known answer detection for all local IPv6 addresses
+ * - Dynamic size of outgoing packet
+ */
+
+/*
+ * Copyright (c) 2015 Verisure Innovation AB
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Erik Ekman <erik@kryo.se>
+ *
+ */
+
+#include "lwip/apps/mdns.h"
+#include "lwip/apps/mdns_priv.h"
+#include "lwip/netif.h"
+#include "lwip/udp.h"
+#include "lwip/ip_addr.h"
+#include "lwip/mem.h"
+#include "lwip/prot/dns.h"
+
+#include <string.h>
+
+#if LWIP_MDNS_RESPONDER
+
+#if (LWIP_IPV4 && !LWIP_IGMP)
+  #error "If you want to use MDNS with IPv4, you have to define LWIP_IGMP=1 in your lwipopts.h"
+#endif
+#if (LWIP_IPV6 && !LWIP_IPV6_MLD)
+#error "If you want to use MDNS with IPv6, you have to define LWIP_IPV6_MLD=1 in your lwipopts.h"
+#endif
+#if (!LWIP_UDP)
+  #error "If you want to use MDNS, you have to define LWIP_UDP=1 in your lwipopts.h"
+#endif
+
+#if LWIP_IPV4
+#include "lwip/igmp.h"
+/* IPv4 multicast group 224.0.0.251 */
+static const ip_addr_t v4group = DNS_MQUERY_IPV4_GROUP_INIT;
+#endif
+
+#if LWIP_IPV6
+#include "lwip/mld6.h"
+/* IPv6 multicast group FF02::FB */
+static const ip_addr_t v6group = DNS_MQUERY_IPV6_GROUP_INIT;
+#endif
+
+#define MDNS_PORT 5353
+#define MDNS_TTL  255
+
+/* Stored offsets to beginning of domain names
+ * Used for compression.
+ */
+#define NUM_DOMAIN_OFFSETS 10
+#define DOMAIN_JUMP_SIZE 2
+#define DOMAIN_JUMP 0xc000
+
+static u8_t mdns_netif_client_id;
+static struct udp_pcb *mdns_pcb;
+
+#define NETIF_TO_HOST(netif) (struct mdns_host*)(netif_get_client_data(netif, mdns_netif_client_id))
+
+#define TOPDOMAIN_LOCAL "local"
+
+#define REVERSE_PTR_TOPDOMAIN "arpa"
+#define REVERSE_PTR_V4_DOMAIN "in-addr"
+#define REVERSE_PTR_V6_DOMAIN "ip6"
+
+#define SRV_PRIORITY 0
+#define SRV_WEIGHT   0
+
+/* Payload size allocated for each outgoing UDP packet */
+#define OUTPACKET_SIZE 500
+
+/* Lookup from hostname -> IPv4 */
+#define REPLY_HOST_A            0x01
+/* Lookup from IPv4/v6 -> hostname */
+#define REPLY_HOST_PTR_V4       0x02
+/* Lookup from hostname -> IPv6 */
+#define REPLY_HOST_AAAA         0x04
+/* Lookup from hostname -> IPv6 */
+#define REPLY_HOST_PTR_V6       0x08
+
+/* Lookup for service types */
+#define REPLY_SERVICE_TYPE_PTR  0x10
+/* Lookup for instances of service */
+#define REPLY_SERVICE_NAME_PTR  0x20
+/* Lookup for location of service instance */
+#define REPLY_SERVICE_SRV       0x40
+/* Lookup for text info on service instance */
+#define REPLY_SERVICE_TXT       0x80
+
+static const char *dnssd_protos[] = {
+    "_udp", /* DNSSD_PROTO_UDP */
+    "_tcp", /* DNSSD_PROTO_TCP */
+};
+
+/** Description of a service */
+struct mdns_service {
+  /** TXT record to answer with */
+  struct mdns_domain txtdata;
+  /** Name of service, like 'myweb' */
+  char name[MDNS_LABEL_MAXLEN + 1];
+  /** Type of service, like '_http' */
+  char service[MDNS_LABEL_MAXLEN + 1];
+  /** Callback function and userdata
+   * to update txtdata buffer */
+  service_get_txt_fn_t txt_fn;
+  void *txt_userdata;
+  /** TTL in seconds of SRV/TXT replies */
+  u32_t dns_ttl;
+  /** Protocol, TCP or UDP */
+  u16_t proto;
+  /** Port of the service */
+  u16_t port;
+};
+
+/** Description of a host/netif */
+struct mdns_host {
+  /** Hostname */
+  char name[MDNS_LABEL_MAXLEN + 1];
+  /** Pointer to services */
+  struct mdns_service *services[MDNS_MAX_SERVICES];
+  /** TTL in seconds of A/AAAA/PTR replies */
+  u32_t dns_ttl;
+};
+
+/** Information about received packet */
+struct mdns_packet {
+  /** Sender IP/port */
+  ip_addr_t source_addr;
+  u16_t source_port;
+  /** If packet was received unicast */
+  u16_t recv_unicast;
+  /** Netif that received the packet */
+  struct netif *netif;
+  /** Packet data */
+  struct pbuf *pbuf;
+  /** Current parsing offset in packet */
+  u16_t parse_offset;
+  /** Identifier. Used in legacy queries */
+  u16_t tx_id;
+  /** Number of questions in packet,
+   *  read from packet header */
+  u16_t questions;
+  /** Number of unparsed questions */
+  u16_t questions_left;
+  /** Number of answers in packet,
+   *  (sum of normal, authorative and additional answers)
+   *  read from packet header */
+  u16_t answers;
+  /** Number of unparsed answers */
+  u16_t answers_left;
+};
+
+/** Information about outgoing packet */
+struct mdns_outpacket {
+  /** Netif to send the packet on */
+  struct netif *netif;
+  /** Packet data */
+  struct pbuf *pbuf;
+  /** Current write offset in packet */
+  u16_t write_offset;
+  /** Identifier. Used in legacy queries */
+  u16_t tx_id;
+  /** Destination IP/port if sent unicast */
+  ip_addr_t dest_addr;
+  u16_t dest_port;
+  /** Number of questions written */
+  u16_t questions;
+  /** Number of normal answers written */
+  u16_t answers;
+  /** Number of additional answers written */
+  u16_t additional;
+  /** Offsets for written domain names in packet.
+   *  Used for compression */
+  u16_t domain_offsets[NUM_DOMAIN_OFFSETS];
+  /** If all answers in packet should set cache_flush bit */
+  u8_t cache_flush;
+  /** If reply should be sent unicast */
+  u8_t unicast_reply;
+  /** If legacy query. (tx_id needed, and write
+   *  question again in reply before answer) */
+  u8_t legacy_query;
+  /* Reply bitmask for host information */
+  u8_t host_replies;
+  /* Bitmask for which reverse IPv6 hosts to answer */
+  u8_t host_reverse_v6_replies;
+  /* Reply bitmask per service */
+  u8_t serv_replies[MDNS_MAX_SERVICES];
+};
+
+/** Domain, type and class.
+ *  Shared between questions and answers */
+struct mdns_rr_info {
+  struct mdns_domain domain;
+  u16_t type;
+  u16_t klass;
+};
+
+struct mdns_question {
+  struct mdns_rr_info info;
+  /** unicast reply requested */
+  u16_t unicast;
+};
+
+struct mdns_answer {
+  struct mdns_rr_info info;
+  /** cache flush command bit */
+  u16_t cache_flush;
+  /* Validity time in seconds */
+  u32_t ttl;
+  /** Length of variable answer */
+  u16_t rd_length;
+  /** Offset of start of variable answer in packet */
+  u16_t rd_offset;
+};
+
+/**
+ * Add a label part to a domain
+ * @param domain The domain to add a label to
+ * @param label The label to add, like &lt;hostname&gt;, 'local', 'com' or ''
+ * @param len The length of the label
+ * @return ERR_OK on success, an err_t otherwise if label too long
+ */
+err_t
+mdns_domain_add_label(struct mdns_domain *domain, const char *label, u8_t len)
+{
+  if (len > MDNS_LABEL_MAXLEN) {
+    return ERR_VAL;
+  }
+  if (len > 0 && (1 + len + domain->length >= MDNS_DOMAIN_MAXLEN)) {
+    return ERR_VAL;
+  }
+  /* Allow only zero marker on last byte */
+  if (len == 0 && (1 + domain->length > MDNS_DOMAIN_MAXLEN)) {
+    return ERR_VAL;
+  }
+  domain->name[domain->length] = len;
+  domain->length++;
+  if (len) {
+    MEMCPY(&domain->name[domain->length], label, len);
+    domain->length += len;
+  }
+  return ERR_OK;
+}
+
+/**
+ * Internal readname function with max 6 levels of recursion following jumps
+ * while decompressing name
+ */
+static u16_t
+mdns_readname_loop(struct pbuf *p, u16_t offset, struct mdns_domain *domain, unsigned depth)
+{
+  u8_t c;
+
+  do {
+    if (depth > 5) {
+      /* Too many jumps */
+      return MDNS_READNAME_ERROR;
+    }
+
+    c = pbuf_get_at(p, offset);
+    offset++;
+
+    /* is this a compressed label? */
+    if((c & 0xc0) == 0xc0) {
+      u16_t jumpaddr;
+      if (offset >= p->tot_len) {
+        /* Make sure both jump bytes fit in the packet */
+        return MDNS_READNAME_ERROR;
+      }
+      jumpaddr = (((c & 0x3f) << 8) | (pbuf_get_at(p, offset) & 0xff));
+      offset++;
+      if (jumpaddr >= SIZEOF_DNS_HDR && jumpaddr < p->tot_len) {
+        u16_t res;
+      /* Recursive call, maximum depth will be checked */
+        res = mdns_readname_loop(p, jumpaddr, domain, depth + 1);
+        /* Dont return offset since new bytes were not read (jumped to somewhere in packet) */
+        if (res == MDNS_READNAME_ERROR) {
+          return res;
+        }
+      } else {
+        return MDNS_READNAME_ERROR;
+      }
+      break;
+    }
+
+    /* normal label */
+    if (c <= MDNS_LABEL_MAXLEN) {
+      u8_t label[MDNS_LABEL_MAXLEN];
+      err_t res;
+
+      if (c + domain->length >= MDNS_DOMAIN_MAXLEN) {
+        return MDNS_READNAME_ERROR;
+      }
+      if (c != 0) {
+        if (pbuf_copy_partial(p, label, c, offset) != c) {
+          return MDNS_READNAME_ERROR;
+        }
+        offset += c;
+      }
+      res = mdns_domain_add_label(domain, (char *) label, c);
+      if (res != ERR_OK) {
+        return MDNS_READNAME_ERROR;
+      }
+    } else {
+      /* bad length byte */
+      return MDNS_READNAME_ERROR;
+    }
+  } while (c != 0);
+
+  return offset;
+}
+
+/**
+ * Read possibly compressed domain name from packet buffer
+ * @param p The packet
+ * @param offset start position of domain name in packet
+ * @param domain The domain name destination
+ * @return The new offset after the domain, or MDNS_READNAME_ERROR
+ *         if reading failed
+ */
+u16_t
+mdns_readname(struct pbuf *p, u16_t offset, struct mdns_domain *domain)
+{
+  memset(domain, 0, sizeof(struct mdns_domain));
+  return mdns_readname_loop(p, offset, domain, 0);
+}
+
+/**
+ * Print domain name to debug output
+ * @param domain The domain name
+ */
+static void
+mdns_domain_debug_print(struct mdns_domain *domain)
+{
+  u8_t *src = domain->name;
+  u8_t i;
+
+  while (*src) {
+    u8_t label_len = *src;
+    src++;
+    for (i = 0; i < label_len; i++) {
+      LWIP_DEBUGF(MDNS_DEBUG, ("%c", src[i]));
+    }
+    src += label_len;
+    LWIP_DEBUGF(MDNS_DEBUG, ("."));
+  }
+}
+
+/**
+ * Return 1 if contents of domains match (case-insensitive)
+ * @param a Domain name to compare 1
+ * @param b Domain name to compare 2
+ * @return 1 if domains are equal ignoring case, 0 otherwise
+ */
+int
+mdns_domain_eq(struct mdns_domain *a, struct mdns_domain *b)
+{
+  u8_t *ptra, *ptrb;
+  u8_t len;
+  int res;
+
+  if (a->length != b->length) {
+    return 0;
+  }
+
+  ptra = a->name;
+  ptrb = b->name;
+  while (*ptra && *ptrb && ptra < &a->name[a->length]) {
+    if (*ptra != *ptrb) {
+      return 0;
+    }
+    len = *ptra;
+    ptra++;
+    ptrb++;
+    res = lwip_strnicmp((char *) ptra, (char *) ptrb, len);
+    if (res != 0) {
+      return 0;
+    }
+    ptra += len;
+    ptrb += len;
+  }
+  if (*ptra != *ptrb && ptra < &a->name[a->length]) {
+    return 0;
+  }
+  return 1;
+}
+
+/**
+ * Call user supplied function to setup TXT data
+ * @param service The service to build TXT record for
+ */
+static void
+mdns_prepare_txtdata(struct mdns_service *service)
+{
+  memset(&service->txtdata, 0, sizeof(struct mdns_domain));
+  if (service->txt_fn) {
+    service->txt_fn(service, service->txt_userdata);
+  }
+}
+
+#if LWIP_IPV4
+/**
+ * Build domain for reverse lookup of IPv4 address
+ * like 12.0.168.192.in-addr.arpa. for 192.168.0.12
+ * @param domain Where to write the domain name
+ * @param addr Pointer to an IPv4 address to encode
+ * @return ERR_OK if domain was written, an err_t otherwise
+ */
+static err_t
+mdns_build_reverse_v4_domain(struct mdns_domain *domain, const ip4_addr_t *addr)
+{
+  int i;
+  err_t res;
+  const u8_t *ptr;
+  if (!domain || !addr) {
+    return ERR_ARG;
+  }
+  memset(domain, 0, sizeof(struct mdns_domain));
+  ptr = (const u8_t *) addr;
+  for (i = sizeof(ip4_addr_t) - 1; i >= 0; i--) {
+    char buf[4];
+    u8_t val = ptr[i];
+
+    lwip_itoa(buf, sizeof(buf), val);
+    res = mdns_domain_add_label(domain, buf, (u8_t)strlen(buf));
+    LWIP_ERROR("mdns_build_reverse_v4_domain: Failed to add label", (res == ERR_OK), return res);
+  }
+  res = mdns_domain_add_label(domain, REVERSE_PTR_V4_DOMAIN, (u8_t)(sizeof(REVERSE_PTR_V4_DOMAIN)-1));
+  LWIP_ERROR("mdns_build_reverse_v4_domain: Failed to add label", (res == ERR_OK), return res);
+  res = mdns_domain_add_label(domain, REVERSE_PTR_TOPDOMAIN, (u8_t)(sizeof(REVERSE_PTR_TOPDOMAIN)-1));
+  LWIP_ERROR("mdns_build_reverse_v4_domain: Failed to add label", (res == ERR_OK), return res);
+  res = mdns_domain_add_label(domain, NULL, 0);
+  LWIP_ERROR("mdns_build_reverse_v4_domain: Failed to add label", (res == ERR_OK), return res);
+
+  return ERR_OK;
+}
+#endif
+
+#if LWIP_IPV6
+/**
+ * Build domain for reverse lookup of IP address
+ * like b.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa. for 2001:db8::567:89ab
+ * @param domain Where to write the domain name
+ * @param addr Pointer to an IPv6 address to encode
+ * @return ERR_OK if domain was written, an err_t otherwise
+ */
+static err_t
+mdns_build_reverse_v6_domain(struct mdns_domain *domain, const ip6_addr_t *addr)
+{
+  int i;
+  err_t res;
+  const u8_t *ptr;
+  if (!domain || !addr) {
+    return ERR_ARG;
+  }
+  memset(domain, 0, sizeof(struct mdns_domain));
+  ptr = (const u8_t *) addr;
+  for (i = sizeof(ip6_addr_t) - 1; i >= 0; i--) {
+    char buf;
+    u8_t byte = ptr[i];
+    int j;
+    for (j = 0; j < 2; j++) {
+      if ((byte & 0x0F) < 0xA) {
+        buf = '0' + (byte & 0x0F);
+      } else {
+        buf = 'a' + (byte & 0x0F) - 0xA;
+      }
+      res = mdns_domain_add_label(domain, &buf, sizeof(buf));
+      LWIP_ERROR("mdns_build_reverse_v6_domain: Failed to add label", (res == ERR_OK), return res);
+      byte >>= 4;
+    }
+  }
+  res = mdns_domain_add_label(domain, REVERSE_PTR_V6_DOMAIN, (u8_t)(sizeof(REVERSE_PTR_V6_DOMAIN)-1));
+  LWIP_ERROR("mdns_build_reverse_v6_domain: Failed to add label", (res == ERR_OK), return res);
+  res = mdns_domain_add_label(domain, REVERSE_PTR_TOPDOMAIN, (u8_t)(sizeof(REVERSE_PTR_TOPDOMAIN)-1));
+  LWIP_ERROR("mdns_build_reverse_v6_domain: Failed to add label", (res == ERR_OK), return res);
+  res = mdns_domain_add_label(domain, NULL, 0);
+  LWIP_ERROR("mdns_build_reverse_v6_domain: Failed to add label", (res == ERR_OK), return res);
+
+  return ERR_OK;
+}
+#endif
+
+/* Add .local. to domain */
+static err_t
+mdns_add_dotlocal(struct mdns_domain *domain)
+{
+  err_t res = mdns_domain_add_label(domain, TOPDOMAIN_LOCAL, (u8_t)(sizeof(TOPDOMAIN_LOCAL)-1));
+  LWIP_ERROR("mdns_add_dotlocal: Failed to add label", (res == ERR_OK), return res);
+  return mdns_domain_add_label(domain, NULL, 0);
+}
+
+/**
+ * Build the <hostname>.local. domain name
+ * @param domain Where to write the domain name
+ * @param mdns TMDNS netif descriptor.
+ * @return ERR_OK if domain <hostname>.local. was written, an err_t otherwise
+ */
+static err_t
+mdns_build_host_domain(struct mdns_domain *domain, struct mdns_host *mdns)
+{
+  err_t res;
+  memset(domain, 0, sizeof(struct mdns_domain));
+  LWIP_ERROR("mdns_build_host_domain: mdns != NULL", (mdns != NULL), return ERR_VAL);
+  res = mdns_domain_add_label(domain, mdns->name, (u8_t)strlen(mdns->name));
+  LWIP_ERROR("mdns_build_host_domain: Failed to add label", (res == ERR_OK), return res);
+  return mdns_add_dotlocal(domain);
+}
+
+/**
+ * Build the lookup-all-services special DNS-SD domain name
+ * @param domain Where to write the domain name
+ * @return ERR_OK if domain _services._dns-sd._udp.local. was written, an err_t otherwise
+ */
+static err_t
+mdns_build_dnssd_domain(struct mdns_domain *domain)
+{
+  err_t res;
+  memset(domain, 0, sizeof(struct mdns_domain));
+  res = mdns_domain_add_label(domain, "_services", (u8_t)(sizeof("_services")-1));
+  LWIP_ERROR("mdns_build_dnssd_domain: Failed to add label", (res == ERR_OK), return res);
+  res = mdns_domain_add_label(domain, "_dns-sd", (u8_t)(sizeof("_dns-sd")-1));
+  LWIP_ERROR("mdns_build_dnssd_domain: Failed to add label", (res == ERR_OK), return res);
+  res = mdns_domain_add_label(domain, dnssd_protos[DNSSD_PROTO_UDP], (u8_t)strlen(dnssd_protos[DNSSD_PROTO_UDP]));
+  LWIP_ERROR("mdns_build_dnssd_domain: Failed to add label", (res == ERR_OK), return res);
+  return mdns_add_dotlocal(domain);
+}
+
+/**
+ * Build domain name for a service
+ * @param domain Where to write the domain name
+ * @param service The service struct, containing service name, type and protocol
+ * @param include_name Whether to include the service name in the domain
+ * @return ERR_OK if domain was written. If service name is included,
+ *         <name>.<type>.<proto>.local. will be written, otherwise <type>.<proto>.local.
+ *         An err_t is returned on error.
+ */
+static err_t
+mdns_build_service_domain(struct mdns_domain *domain, struct mdns_service *service, int include_name)
+{
+  err_t res;
+  memset(domain, 0, sizeof(struct mdns_domain));
+  if (include_name) {
+    res = mdns_domain_add_label(domain, service->name, (u8_t)strlen(service->name));
+    LWIP_ERROR("mdns_build_service_domain: Failed to add label", (res == ERR_OK), return res);
+  }
+  res = mdns_domain_add_label(domain, service->service, (u8_t)strlen(service->service));
+  LWIP_ERROR("mdns_build_service_domain: Failed to add label", (res == ERR_OK), return res);
+  res = mdns_domain_add_label(domain, dnssd_protos[service->proto], (u8_t)strlen(dnssd_protos[service->proto]));
+  LWIP_ERROR("mdns_build_service_domain: Failed to add label", (res == ERR_OK), return res);
+  return mdns_add_dotlocal(domain);
+}
+
+/**
+ * Check which replies we should send for a host/netif based on question
+ * @param netif The network interface that received the question
+ * @param rr Domain/type/class from a question
+ * @param reverse_v6_reply Bitmask of which IPv6 addresses to send reverse PTRs for
+ *                         if reply bit has REPLY_HOST_PTR_V6 set
+ * @return Bitmask of which replies to send
+ */
+static int
+check_host(struct netif *netif, struct mdns_rr_info *rr, u8_t *reverse_v6_reply)
+{
+  err_t res;
+  int replies = 0;
+  struct mdns_domain mydomain;
+
+  LWIP_UNUSED_ARG(reverse_v6_reply); /* if ipv6 is disabled */
+
+  if (rr->klass != DNS_RRCLASS_IN && rr->klass != DNS_RRCLASS_ANY) {
+    /* Invalid class */
+    return replies;
+  }
+
+  /* Handle PTR for our addresses */
+  if (rr->type == DNS_RRTYPE_PTR || rr->type == DNS_RRTYPE_ANY) {
+#if LWIP_IPV6
+    int i;
+    for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+      if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i))) {
+        res = mdns_build_reverse_v6_domain(&mydomain, netif_ip6_addr(netif, i));
+        if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain)) {
+          replies |= REPLY_HOST_PTR_V6;
+          /* Mark which addresses where requested */
+          if (reverse_v6_reply) {
+            *reverse_v6_reply |= (1 << i);
+          }
+        }
+      }
+    }
+#endif
+#if LWIP_IPV4
+    if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) {
+      res = mdns_build_reverse_v4_domain(&mydomain, netif_ip4_addr(netif));
+      if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain)) {
+        replies |= REPLY_HOST_PTR_V4;
+      }
+    }
+#endif
+  }
+
+  res = mdns_build_host_domain(&mydomain, NETIF_TO_HOST(netif));
+  /* Handle requests for our hostname */
+  if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain)) {
+    /* TODO return NSEC if unsupported protocol requested */
+#if LWIP_IPV4
+    if (!ip4_addr_isany_val(*netif_ip4_addr(netif))
+        && (rr->type == DNS_RRTYPE_A || rr->type == DNS_RRTYPE_ANY)) {
+      replies |= REPLY_HOST_A;
+    }
+#endif
+#if LWIP_IPV6
+    if (rr->type == DNS_RRTYPE_AAAA || rr->type == DNS_RRTYPE_ANY) {
+      replies |= REPLY_HOST_AAAA;
+    }
+#endif
+  }
+
+  return replies;
+}
+
+/**
+ * Check which replies we should send for a service based on question
+ * @param service A registered MDNS service
+ * @param rr Domain/type/class from a question
+ * @return Bitmask of which replies to send
+ */
+static int
+check_service(struct mdns_service *service, struct mdns_rr_info *rr)
+{
+  err_t res;
+  int replies = 0;
+  struct mdns_domain mydomain;
+
+  if (rr->klass != DNS_RRCLASS_IN && rr->klass != DNS_RRCLASS_ANY) {
+    /* Invalid class */
+    return 0;
+  }
+
+  res = mdns_build_dnssd_domain(&mydomain);
+  if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain) &&
+      (rr->type == DNS_RRTYPE_PTR || rr->type == DNS_RRTYPE_ANY)) {
+    /* Request for all service types */
+    replies |= REPLY_SERVICE_TYPE_PTR;
+  }
+
+  res = mdns_build_service_domain(&mydomain, service, 0);
+  if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain) &&
+      (rr->type == DNS_RRTYPE_PTR || rr->type == DNS_RRTYPE_ANY)) {
+    /* Request for the instance of my service */
+    replies |= REPLY_SERVICE_NAME_PTR;
+  }
+
+  res = mdns_build_service_domain(&mydomain, service, 1);
+  if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain)) {
+    /* Request for info about my service */
+    if (rr->type == DNS_RRTYPE_SRV || rr->type == DNS_RRTYPE_ANY) {
+      replies |= REPLY_SERVICE_SRV;
+    }
+    if (rr->type == DNS_RRTYPE_TXT || rr->type == DNS_RRTYPE_ANY) {
+      replies |= REPLY_SERVICE_TXT;
+    }
+  }
+
+  return replies;
+}
+
+/**
+ * Return bytes needed to write before jump for best result of compressing supplied domain
+ * against domain in outpacket starting at specified offset.
+ * If a match is found, offset is updated to where to jump to
+ * @param pbuf Pointer to pbuf with the partially constructed DNS packet
+ * @param offset Start position of a domain written earlier. If this location is suitable
+ *               for compression, the pointer is updated to where in the domain to jump to.
+ * @param domain The domain to write
+ * @return Number of bytes to write of the new domain before writing a jump to the offset.
+ *         If compression can not be done against this previous domain name, the full new
+ *         domain length is returned.
+ */
+u16_t
+mdns_compress_domain(struct pbuf *pbuf, u16_t *offset, struct mdns_domain *domain)
+{
+  struct mdns_domain target;
+  u16_t target_end;
+  u8_t target_len;
+  u8_t writelen = 0;
+  u8_t *ptr;
+  if (pbuf == NULL) {
+    return domain->length;
+  }
+  target_end = mdns_readname(pbuf, *offset, &target);
+  if (target_end == MDNS_READNAME_ERROR) {
+    return domain->length;
+  }
+  target_len = (u8_t)(target_end - *offset);
+  ptr = domain->name;
+  while (writelen < domain->length) {
+    u8_t domainlen = (u8_t)(domain->length - writelen);
+    u8_t labellen;
+    if (domainlen <= target.length && domainlen > DOMAIN_JUMP_SIZE) {
+      /* Compare domains if target is long enough, and we have enough left of the domain */
+      u8_t targetpos = (u8_t)(target.length - domainlen);
+      if ((targetpos + DOMAIN_JUMP_SIZE) >= target_len) {
+        /* We are checking at or beyond a jump in the original, stop looking */
+        break;
+      }
+      if (target.length >= domainlen &&
+          memcmp(&domain->name[writelen], &target.name[targetpos], domainlen) == 0) {
+        *offset += targetpos;
+        return writelen;
+      }
+    }
+    /* Skip to next label in domain */
+    labellen = *ptr;
+    writelen += 1 + labellen;
+    ptr += 1 + labellen;
+  }
+  /* Nothing found */
+  return domain->length;
+}
+
+/**
+ * Write domain to outpacket. Compression will be attempted,
+ * unless domain->skip_compression is set.
+ * @param outpkt The outpacket to write to
+ * @param domain The domain name to write
+ * @return ERR_OK on success, an err_t otherwise
+ */
+static err_t
+mdns_write_domain(struct mdns_outpacket *outpkt, struct mdns_domain *domain)
+{
+  int i;
+  err_t res;
+  u16_t writelen = domain->length;
+  u16_t jump_offset = 0;
+  u16_t jump;
+
+  if (!domain->skip_compression) {
+    for (i = 0; i < NUM_DOMAIN_OFFSETS; ++i) {
+      u16_t offset = outpkt->domain_offsets[i];
+      if (offset) {
+        u16_t len = mdns_compress_domain(outpkt->pbuf, &offset, domain);
+        if (len < writelen) {
+          writelen = len;
+          jump_offset = offset;
+        }
+      }
+    }
+  }
+
+  if (writelen) {
+    /* Write uncompressed part of name */
+    res = pbuf_take_at(outpkt->pbuf, domain->name, writelen, outpkt->write_offset);
+    if (res != ERR_OK) {
+      return res;
+    }
+
+    /* Store offset of this new domain */
+    for (i = 0; i < NUM_DOMAIN_OFFSETS; ++i) {
+      if (outpkt->domain_offsets[i] == 0) {
+        outpkt->domain_offsets[i] = outpkt->write_offset;
+        break;
+      }
+    }
+
+    outpkt->write_offset += writelen;
+  }
+  if (jump_offset) {
+    /* Write jump */
+    jump = lwip_htons(DOMAIN_JUMP | jump_offset);
+    res = pbuf_take_at(outpkt->pbuf, &jump, DOMAIN_JUMP_SIZE, outpkt->write_offset);
+    if (res != ERR_OK) {
+      return res;
+    }
+    outpkt->write_offset += DOMAIN_JUMP_SIZE;
+  }
+  return ERR_OK;
+}
+
+/**
+ * Write a question to an outpacket
+ * A question contains domain, type and class. Since an answer also starts with these fields this function is also
+ * called from mdns_add_answer().
+ * @param outpkt The outpacket to write to
+ * @param domain The domain name the answer is for
+ * @param type The DNS type of the answer (like 'AAAA', 'SRV')
+ * @param klass The DNS type of the answer (like 'IN')
+ * @param unicast If highest bit in class should be set, to instruct the responder to
+ *                reply with a unicast packet
+ * @return ERR_OK on success, an err_t otherwise
+ */
+static err_t
+mdns_add_question(struct mdns_outpacket *outpkt, struct mdns_domain *domain, u16_t type, u16_t klass, u16_t unicast)
+{
+  u16_t question_len;
+  u16_t field16;
+  err_t res;
+
+  if (!outpkt->pbuf) {
+    /* If no pbuf is active, allocate one */
+    outpkt->pbuf = pbuf_alloc(PBUF_TRANSPORT, OUTPACKET_SIZE, PBUF_RAM);
+    if (!outpkt->pbuf) {
+      return ERR_MEM;
+    }
+    outpkt->write_offset = SIZEOF_DNS_HDR;
+  }
+
+  /* Worst case calculation. Domain string might be compressed */
+  question_len = domain->length + sizeof(type) + sizeof(klass);
+  if (outpkt->write_offset + question_len > outpkt->pbuf->tot_len) {
+    /* No space */
+    return ERR_MEM;
+  }
+
+  /* Write name */
+  res = mdns_write_domain(outpkt, domain);
+  if (res != ERR_OK) {
+    return res;
+  }
+
+  /* Write type */
+  field16 = lwip_htons(type);
+  res = pbuf_take_at(outpkt->pbuf, &field16, sizeof(field16), outpkt->write_offset);
+  if (res != ERR_OK) {
+    return res;
+  }
+  outpkt->write_offset += sizeof(field16);
+
+  /* Write class */
+  if (unicast) {
+    klass |= 0x8000;
+  }
+  field16 = lwip_htons(klass);
+  res = pbuf_take_at(outpkt->pbuf, &field16, sizeof(field16), outpkt->write_offset);
+  if (res != ERR_OK) {
+    return res;
+  }
+  outpkt->write_offset += sizeof(field16);
+
+  return ERR_OK;
+}
+
+/**
+ * Write answer to reply packet.
+ * buf or answer_domain can be null. The rd_length written will be buf_length +
+ * size of (compressed) domain. Most uses will need either buf or answer_domain,
+ * special case is SRV that starts with 3 u16 and then a domain name.
+ * @param reply The outpacket to write to
+ * @param domain The domain name the answer is for
+ * @param type The DNS type of the answer (like 'AAAA', 'SRV')
+ * @param klass The DNS type of the answer (like 'IN')
+ * @param cache_flush If highest bit in class should be set, to instruct receiver that
+ *                    this reply replaces any earlier answer for this domain/type/class
+ * @param ttl Validity time in seconds to send out for IP address data in DNS replies
+ * @param buf Pointer to buffer of answer data
+ * @param buf_length Length of variable data
+ * @param answer_domain A domain to write after any buffer data as answer
+ * @return ERR_OK on success, an err_t otherwise
+ */
+static err_t
+mdns_add_answer(struct mdns_outpacket *reply, struct mdns_domain *domain, u16_t type, u16_t klass, u16_t cache_flush,
+                u32_t ttl, const u8_t *buf, size_t buf_length, struct mdns_domain *answer_domain)
+{
+  u16_t answer_len;
+  u16_t field16;
+  u16_t rdlen_offset;
+  u16_t answer_offset;
+  u32_t field32;
+  err_t res;
+
+  if (!reply->pbuf) {
+    /* If no pbuf is active, allocate one */
+    reply->pbuf = pbuf_alloc(PBUF_TRANSPORT, OUTPACKET_SIZE, PBUF_RAM);
+    if (!reply->pbuf) {
+      return ERR_MEM;
+    }
+    reply->write_offset = SIZEOF_DNS_HDR;
+  }
+
+  /* Worst case calculation. Domain strings might be compressed */
+  answer_len = domain->length + sizeof(type) + sizeof(klass) + sizeof(ttl) + sizeof(field16)/*rd_length*/;
+  if (buf) {
+    answer_len += (u16_t)buf_length;
+  }
+  if (answer_domain) {
+    answer_len += answer_domain->length;
+  }
+  if (reply->write_offset + answer_len > reply->pbuf->tot_len) {
+    /* No space */
+    return ERR_MEM;
+  }
+
+  /* Answer starts with same data as question, then more fields */
+  mdns_add_question(reply, domain, type, klass, cache_flush);
+
+  /* Write TTL */
+  field32 = lwip_htonl(ttl);
+  res = pbuf_take_at(reply->pbuf, &field32, sizeof(field32), reply->write_offset);
+  if (res != ERR_OK) {
+    return res;
+  }
+  reply->write_offset += sizeof(field32);
+
+  /* Store offsets and skip forward to the data */
+  rdlen_offset = reply->write_offset;
+  reply->write_offset += sizeof(field16);
+  answer_offset = reply->write_offset;
+
+  if (buf) {
+    /* Write static data */
+    res = pbuf_take_at(reply->pbuf, buf, (u16_t)buf_length, reply->write_offset);
+    if (res != ERR_OK) {
+      return res;
+    }
+    reply->write_offset += (u16_t)buf_length;
+  }
+
+  if (answer_domain) {
+    /* Write name answer (compressed if possible) */
+    res = mdns_write_domain(reply, answer_domain);
+    if (res != ERR_OK) {
+      return res;
+    }
+  }
+
+  /* Write rd_length after when we know the answer size */
+  field16 = lwip_htons(reply->write_offset - answer_offset);
+  res = pbuf_take_at(reply->pbuf, &field16, sizeof(field16), rdlen_offset);
+
+  return res;
+}
+
+/**
+ * Helper function for mdns_read_question/mdns_read_answer
+ * Reads a domain, type and class from the packet
+ * @param pkt The MDNS packet to read from. The parse_offset field will be
+ *            incremented to point to the next unparsed byte.
+ * @param info The struct to fill with domain, type and class
+ * @return ERR_OK on success, an err_t otherwise
+ */
+static err_t
+mdns_read_rr_info(struct mdns_packet *pkt, struct mdns_rr_info *info)
+{
+  u16_t field16, copied;
+  pkt->parse_offset = mdns_readname(pkt->pbuf, pkt->parse_offset, &info->domain);
+  if (pkt->parse_offset == MDNS_READNAME_ERROR) {
+    return ERR_VAL;
+  }
+
+  copied = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), pkt->parse_offset);
+  if (copied != sizeof(field16)) {
+    return ERR_VAL;
+  }
+  pkt->parse_offset += copied;
+  info->type = lwip_ntohs(field16);
+
+  copied = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), pkt->parse_offset);
+  if (copied != sizeof(field16)) {
+    return ERR_VAL;
+  }
+  pkt->parse_offset += copied;
+  info->klass = lwip_ntohs(field16);
+
+  return ERR_OK;
+}
+
+/**
+ * Read a question from the packet.
+ * All questions have to be read before the answers.
+ * @param pkt The MDNS packet to read from. The questions_left field will be decremented
+ *            and the parse_offset will be updated.
+ * @param question The struct to fill with question data
+ * @return ERR_OK on success, an err_t otherwise
+ */
+static err_t
+mdns_read_question(struct mdns_packet *pkt, struct mdns_question *question)
+{
+  /* Safety check */
+  if (pkt->pbuf->tot_len < pkt->parse_offset) {
+    return ERR_VAL;
+  }
+
+  if (pkt->questions_left) {
+    err_t res;
+    pkt->questions_left--;
+
+    memset(question, 0, sizeof(struct mdns_question));
+    res = mdns_read_rr_info(pkt, &question->info);
+    if (res != ERR_OK) {
+      return res;
+    }
+
+    /* Extract unicast flag from class field */
+    question->unicast = question->info.klass & 0x8000;
+    question->info.klass &= 0x7FFF;
+
+    return ERR_OK;
+  }
+  return ERR_VAL;
+}
+
+/**
+ * Read an answer from the packet
+ * The variable length reply is not copied, its pbuf offset and length is stored instead.
+ * @param pkt The MDNS packet to read. The answers_left field will be decremented and
+ *            the parse_offset will be updated.
+ * @param answer The struct to fill with answer data
+ * @return ERR_OK on success, an err_t otherwise
+ */
+static err_t
+mdns_read_answer(struct mdns_packet *pkt, struct mdns_answer *answer)
+{
+  /* Read questions first */
+  if (pkt->questions_left) {
+    return ERR_VAL;
+  }
+
+  /* Safety check */
+  if (pkt->pbuf->tot_len < pkt->parse_offset) {
+    return ERR_VAL;
+  }
+
+  if (pkt->answers_left) {
+    u16_t copied, field16;
+    u32_t ttl;
+    err_t res;
+    pkt->answers_left--;
+
+    memset(answer, 0, sizeof(struct mdns_answer));
+    res = mdns_read_rr_info(pkt, &answer->info);
+    if (res != ERR_OK) {
+      return res;
+    }
+
+    /* Extract cache_flush flag from class field */
+    answer->cache_flush = answer->info.klass & 0x8000;
+    answer->info.klass &= 0x7FFF;
+
+    copied = pbuf_copy_partial(pkt->pbuf, &ttl, sizeof(ttl), pkt->parse_offset);
+    if (copied != sizeof(ttl)) {
+      return ERR_VAL;
+    }
+    pkt->parse_offset += copied;
+    answer->ttl = lwip_ntohl(ttl);
+
+    copied = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), pkt->parse_offset);
+    if (copied != sizeof(field16)) {
+      return ERR_VAL;
+    }
+    pkt->parse_offset += copied;
+    answer->rd_length = lwip_ntohs(field16);
+
+    answer->rd_offset = pkt->parse_offset;
+    pkt->parse_offset += answer->rd_length;
+
+    return ERR_OK;
+  }
+  return ERR_VAL;
+}
+
+#if LWIP_IPV4
+/** Write an IPv4 address (A) RR to outpacket */
+static err_t
+mdns_add_a_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct netif *netif)
+{
+  struct mdns_domain host;
+  mdns_build_host_domain(&host, NETIF_TO_HOST(netif));
+  LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with A record\n"));
+  return mdns_add_answer(reply, &host, DNS_RRTYPE_A, DNS_RRCLASS_IN, cache_flush, (NETIF_TO_HOST(netif))->dns_ttl, (const u8_t *) netif_ip4_addr(netif), sizeof(ip4_addr_t), NULL);
+}
+
+/** Write a 4.3.2.1.in-addr.arpa -> hostname.local PTR RR to outpacket */
+static err_t
+mdns_add_hostv4_ptr_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct netif *netif)
+{
+  struct mdns_domain host, revhost;
+  mdns_build_host_domain(&host, NETIF_TO_HOST(netif));
+  mdns_build_reverse_v4_domain(&revhost, netif_ip4_addr(netif));
+  LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with v4 PTR record\n"));
+  return mdns_add_answer(reply, &revhost, DNS_RRTYPE_PTR, DNS_RRCLASS_IN, cache_flush, (NETIF_TO_HOST(netif))->dns_ttl, NULL, 0, &host);
+}
+#endif
+
+#if LWIP_IPV6
+/** Write an IPv6 address (AAAA) RR to outpacket */
+static err_t
+mdns_add_aaaa_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct netif *netif, int addrindex)
+{
+  struct mdns_domain host;
+  mdns_build_host_domain(&host, NETIF_TO_HOST(netif));
+  LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with AAAA record\n"));
+  return mdns_add_answer(reply, &host, DNS_RRTYPE_AAAA, DNS_RRCLASS_IN, cache_flush, (NETIF_TO_HOST(netif))->dns_ttl, (const u8_t *) netif_ip6_addr(netif, addrindex), sizeof(ip6_addr_t), NULL);
+}
+
+/** Write a x.y.z.ip6.arpa -> hostname.local PTR RR to outpacket */
+static err_t
+mdns_add_hostv6_ptr_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct netif *netif, int addrindex)
+{
+  struct mdns_domain host, revhost;
+  mdns_build_host_domain(&host, NETIF_TO_HOST(netif));
+  mdns_build_reverse_v6_domain(&revhost, netif_ip6_addr(netif, addrindex));
+  LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with v6 PTR record\n"));
+  return mdns_add_answer(reply, &revhost, DNS_RRTYPE_PTR, DNS_RRCLASS_IN, cache_flush, (NETIF_TO_HOST(netif))->dns_ttl, NULL, 0, &host);
+}
+#endif
+
+/** Write an all-services -> servicetype PTR RR to outpacket */
+static err_t
+mdns_add_servicetype_ptr_answer(struct mdns_outpacket *reply, struct mdns_service *service)
+{
+  struct mdns_domain service_type, service_dnssd;
+  mdns_build_service_domain(&service_type, service, 0);
+  mdns_build_dnssd_domain(&service_dnssd);
+  LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with service type PTR record\n"));
+  return mdns_add_answer(reply, &service_dnssd, DNS_RRTYPE_PTR, DNS_RRCLASS_IN, 0, service->dns_ttl, NULL, 0, &service_type);
+}
+
+/** Write a servicetype -> servicename PTR RR to outpacket */
+static err_t
+mdns_add_servicename_ptr_answer(struct mdns_outpacket *reply, struct mdns_service *service)
+{
+  struct mdns_domain service_type, service_instance;
+  mdns_build_service_domain(&service_type, service, 0);
+  mdns_build_service_domain(&service_instance, service, 1);
+  LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with service name PTR record\n"));
+  return mdns_add_answer(reply, &service_type, DNS_RRTYPE_PTR, DNS_RRCLASS_IN, 0, service->dns_ttl, NULL, 0, &service_instance);
+}
+
+/** Write a SRV RR to outpacket */
+static err_t
+mdns_add_srv_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct mdns_host *mdns, struct mdns_service *service)
+{
+  struct mdns_domain service_instance, srvhost;
+  u16_t srvdata[3];
+  mdns_build_service_domain(&service_instance, service, 1);
+  mdns_build_host_domain(&srvhost, mdns);
+  if (reply->legacy_query) {
+    /* RFC 6762 section 18.14:
+     * In legacy unicast responses generated to answer legacy queries,
+     * name compression MUST NOT be performed on SRV records.
+     */
+    srvhost.skip_compression = 1;
+  }
+  srvdata[0] = lwip_htons(SRV_PRIORITY);
+  srvdata[1] = lwip_htons(SRV_WEIGHT);
+  srvdata[2] = lwip_htons(service->port);
+  LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with SRV record\n"));
+  return mdns_add_answer(reply, &service_instance, DNS_RRTYPE_SRV, DNS_RRCLASS_IN, cache_flush, service->dns_ttl,
+                         (const u8_t *) &srvdata, sizeof(srvdata), &srvhost);
+}
+
+/** Write a TXT RR to outpacket */
+static err_t
+mdns_add_txt_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct mdns_service *service)
+{
+  struct mdns_domain service_instance;
+  mdns_build_service_domain(&service_instance, service, 1);
+  mdns_prepare_txtdata(service);
+  LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with TXT record\n"));
+  return mdns_add_answer(reply, &service_instance, DNS_RRTYPE_TXT, DNS_RRCLASS_IN, cache_flush, service->dns_ttl,
+                         (u8_t *) &service->txtdata.name, service->txtdata.length, NULL);
+}
+
+/**
+ * Setup outpacket as a reply to the incoming packet
+ */
+static void
+mdns_init_outpacket(struct mdns_outpacket *out, struct mdns_packet *in)
+{
+  memset(out, 0, sizeof(struct mdns_outpacket));
+  out->cache_flush = 1;
+  out->netif = in->netif;
+
+  /* Copy source IP/port to use when responding unicast, or to choose
+   * which pcb to use for multicast (IPv4/IPv6)
+   */
+  SMEMCPY(&out->dest_addr, &in->source_addr, sizeof(ip_addr_t));
+  out->dest_port = in->source_port;
+
+  if (in->source_port != MDNS_PORT) {
+    out->unicast_reply = 1;
+    out->cache_flush = 0;
+    if (in->questions == 1) {
+      out->legacy_query = 1;
+      out->tx_id = in->tx_id;
+    }
+  }
+
+  if (in->recv_unicast) {
+    out->unicast_reply = 1;
+  }
+}
+
+/**
+ * Send chosen answers as a reply
+ *
+ * Add all selected answers (first write will allocate pbuf)
+ * Add additional answers based on the selected answers
+ * Send the packet
+ */
+static void
+mdns_send_outpacket(struct mdns_outpacket *outpkt)
+{
+  struct mdns_service *service;
+  err_t res;
+  int i;
+  struct mdns_host* mdns = NETIF_TO_HOST(outpkt->netif);
+
+  /* Write answers to host questions */
+#if LWIP_IPV4
+  if (outpkt->host_replies & REPLY_HOST_A) {
+    res = mdns_add_a_answer(outpkt, outpkt->cache_flush, outpkt->netif);
+    if (res != ERR_OK) {
+      goto cleanup;
+    }
+    outpkt->answers++;
+  }
+  if (outpkt->host_replies & REPLY_HOST_PTR_V4) {
+    res = mdns_add_hostv4_ptr_answer(outpkt, outpkt->cache_flush, outpkt->netif);
+    if (res != ERR_OK) {
+      goto cleanup;
+    }
+    outpkt->answers++;
+  }
+#endif
+#if LWIP_IPV6
+  if (outpkt->host_replies & REPLY_HOST_AAAA) {
+    int addrindex;
+    for (addrindex = 0; addrindex < LWIP_IPV6_NUM_ADDRESSES; ++addrindex) {
+      if (ip6_addr_isvalid(netif_ip6_addr_state(outpkt->netif, addrindex))) {
+        res = mdns_add_aaaa_answer(outpkt, outpkt->cache_flush, outpkt->netif, addrindex);
+        if (res != ERR_OK) {
+          goto cleanup;
+        }
+        outpkt->answers++;
+      }
+    }
+  }
+  if (outpkt->host_replies & REPLY_HOST_PTR_V6) {
+    u8_t rev_addrs = outpkt->host_reverse_v6_replies;
+    int addrindex = 0;
+    while (rev_addrs) {
+      if (rev_addrs & 1) {
+        res = mdns_add_hostv6_ptr_answer(outpkt, outpkt->cache_flush, outpkt->netif, addrindex);
+        if (res != ERR_OK) {
+          goto cleanup;
+        }
+        outpkt->answers++;
+      }
+      addrindex++;
+      rev_addrs >>= 1;
+    }
+  }
+#endif
+
+  /* Write answers to service questions */
+  for (i = 0; i < MDNS_MAX_SERVICES; ++i) {
+    service = mdns->services[i];
+    if (!service) {
+      continue;
+    }
+
+    if (outpkt->serv_replies[i] & REPLY_SERVICE_TYPE_PTR) {
+      res = mdns_add_servicetype_ptr_answer(outpkt, service);
+      if (res != ERR_OK) {
+        goto cleanup;
+      }
+      outpkt->answers++;
+    }
+
+    if (outpkt->serv_replies[i] & REPLY_SERVICE_NAME_PTR) {
+      res = mdns_add_servicename_ptr_answer(outpkt, service);
+      if (res != ERR_OK) {
+        goto cleanup;
+      }
+      outpkt->answers++;
+    }
+
+    if (outpkt->serv_replies[i] & REPLY_SERVICE_SRV) {
+      res = mdns_add_srv_answer(outpkt, outpkt->cache_flush, mdns, service);
+      if (res != ERR_OK) {
+        goto cleanup;
+      }
+      outpkt->answers++;
+    }
+
+    if (outpkt->serv_replies[i] & REPLY_SERVICE_TXT) {
+      res = mdns_add_txt_answer(outpkt, outpkt->cache_flush, service);
+      if (res != ERR_OK) {
+        goto cleanup;
+      }
+      outpkt->answers++;
+    }
+  }
+
+  /* All answers written, add additional RRs */
+  for (i = 0; i < MDNS_MAX_SERVICES; ++i) {
+    service = mdns->services[i];
+    if (!service) {
+      continue;
+    }
+
+    if (outpkt->serv_replies[i] & REPLY_SERVICE_NAME_PTR) {
+      /* Our service instance requested, include SRV & TXT
+       * if they are already not requested. */
+      if (!(outpkt->serv_replies[i] & REPLY_SERVICE_SRV)) {
+        res = mdns_add_srv_answer(outpkt, outpkt->cache_flush, mdns, service);
+        if (res != ERR_OK) {
+          goto cleanup;
+        }
+        outpkt->additional++;
+      }
+
+      if (!(outpkt->serv_replies[i] & REPLY_SERVICE_TXT)) {
+        res = mdns_add_txt_answer(outpkt, outpkt->cache_flush, service);
+        if (res != ERR_OK) {
+          goto cleanup;
+        }
+        outpkt->additional++;
+      }
+    }
+
+    /* If service instance, SRV, record or an IP address is requested,
+     * supply all addresses for the host
+     */
+    if ((outpkt->serv_replies[i] & (REPLY_SERVICE_NAME_PTR | REPLY_SERVICE_SRV)) ||
+        (outpkt->host_replies & (REPLY_HOST_A | REPLY_HOST_AAAA))) {
+#if LWIP_IPV6
+      if (!(outpkt->host_replies & REPLY_HOST_AAAA)) {
+        int addrindex;
+        for (addrindex = 0; addrindex < LWIP_IPV6_NUM_ADDRESSES; ++addrindex) {
+          if (ip6_addr_isvalid(netif_ip6_addr_state(outpkt->netif, addrindex))) {
+            res = mdns_add_aaaa_answer(outpkt, outpkt->cache_flush, outpkt->netif, addrindex);
+            if (res != ERR_OK) {
+              goto cleanup;
+            }
+            outpkt->additional++;
+          }
+        }
+      }
+#endif
+#if LWIP_IPV4
+      if (!(outpkt->host_replies & REPLY_HOST_A)) {
+        res = mdns_add_a_answer(outpkt, outpkt->cache_flush, outpkt->netif);
+        if (res != ERR_OK) {
+          goto cleanup;
+        }
+        outpkt->additional++;
+      }
+#endif
+    }
+  }
+
+  if (outpkt->pbuf) {
+    const ip_addr_t *mcast_destaddr;
+    struct dns_hdr hdr;
+
+    /* Write header */
+    memset(&hdr, 0, sizeof(hdr));
+    hdr.flags1 = DNS_FLAG1_RESPONSE | DNS_FLAG1_AUTHORATIVE;
+    hdr.numanswers = lwip_htons(outpkt->answers);
+    hdr.numextrarr = lwip_htons(outpkt->additional);
+    if (outpkt->legacy_query) {
+      hdr.numquestions = lwip_htons(1);
+      hdr.id = lwip_htons(outpkt->tx_id);
+    }
+    pbuf_take(outpkt->pbuf, &hdr, sizeof(hdr));
+
+    /* Shrink packet */
+    pbuf_realloc(outpkt->pbuf, outpkt->write_offset);
+
+    if (IP_IS_V6_VAL(outpkt->dest_addr)) {
+#if LWIP_IPV6
+      mcast_destaddr = &v6group;
+#endif
+    } else {
+#if LWIP_IPV4
+      mcast_destaddr = &v4group;
+#endif
+    }
+    /* Send created packet */
+    LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Sending packet, len=%d, unicast=%d\n", outpkt->write_offset, outpkt->unicast_reply));
+    if (outpkt->unicast_reply) {
+      udp_sendto_if(mdns_pcb, outpkt->pbuf, &outpkt->dest_addr, outpkt->dest_port, outpkt->netif);
+    } else {
+      udp_sendto_if(mdns_pcb, outpkt->pbuf, mcast_destaddr, MDNS_PORT, outpkt->netif);
+    }
+  }
+
+cleanup:
+  if (outpkt->pbuf) {
+    pbuf_free(outpkt->pbuf);
+    outpkt->pbuf = NULL;
+  }
+}
+
+/**
+ * Send unsolicited answer containing all our known data
+ * @param netif The network interface to send on
+ * @param destination The target address to send to (usually multicast address)
+ */
+static void
+mdns_announce(struct netif *netif, const ip_addr_t *destination)
+{
+  struct mdns_outpacket announce;
+  int i;
+  struct mdns_host* mdns = NETIF_TO_HOST(netif);
+
+  memset(&announce, 0, sizeof(announce));
+  announce.netif = netif;
+  announce.cache_flush = 1;
+#if LWIP_IPV4
+  if (!ip4_addr_isany_val(*netif_ip4_addr(netif)))
+    announce.host_replies = REPLY_HOST_A | REPLY_HOST_PTR_V4;
+#endif
+#if LWIP_IPV6
+  for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) {
+    if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i))) {
+      announce.host_replies |= REPLY_HOST_AAAA | REPLY_HOST_PTR_V6;
+      announce.host_reverse_v6_replies |= (1 << i);
+    }
+  }
+#endif
+
+  for (i = 0; i < MDNS_MAX_SERVICES; i++) {
+    struct mdns_service *serv = mdns->services[i];
+    if (serv) {
+      announce.serv_replies[i] = REPLY_SERVICE_TYPE_PTR | REPLY_SERVICE_NAME_PTR |
+          REPLY_SERVICE_SRV | REPLY_SERVICE_TXT;
+    }
+  }
+
+  announce.dest_port = MDNS_PORT;
+  SMEMCPY(&announce.dest_addr, destination, sizeof(announce.dest_addr));
+  mdns_send_outpacket(&announce);
+}
+
+/**
+ * Handle question MDNS packet
+ * 1. Parse all questions and set bits what answers to send
+ * 2. Clear pending answers if known answers are supplied
+ * 3. Put chosen answers in new packet and send as reply
+ */
+static void
+mdns_handle_question(struct mdns_packet *pkt)
+{
+  struct mdns_service *service;
+  struct mdns_outpacket reply;
+  int replies = 0;
+  int i;
+  err_t res;
+  struct mdns_host* mdns = NETIF_TO_HOST(pkt->netif);
+
+  mdns_init_outpacket(&reply, pkt);
+
+  while (pkt->questions_left) {
+    struct mdns_question q;
+
+    res = mdns_read_question(pkt, &q);
+    if (res != ERR_OK) {
+      LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Failed to parse question, skipping query packet\n"));
+      return;
+    }
+
+    LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Query for domain "));
+    mdns_domain_debug_print(&q.info.domain);
+    LWIP_DEBUGF(MDNS_DEBUG, (" type %d class %d\n", q.info.type, q.info.klass));
+
+    if (q.unicast) {
+      /* Reply unicast if any question is unicast */
+      reply.unicast_reply = 1;
+    }
+
+    reply.host_replies |= check_host(pkt->netif, &q.info, &reply.host_reverse_v6_replies);
+    replies |= reply.host_replies;
+
+    for (i = 0; i < MDNS_MAX_SERVICES; ++i) {
+      service = mdns->services[i];
+      if (!service) {
+        continue;
+      }
+      reply.serv_replies[i] |= check_service(service, &q.info);
+      replies |= reply.serv_replies[i];
+    }
+
+    if (replies && reply.legacy_query) {
+      /* Add question to reply packet (legacy packet only has 1 question) */
+      res = mdns_add_question(&reply, &q.info.domain, q.info.type, q.info.klass, 0);
+      if (res != ERR_OK) {
+        goto cleanup;
+      }
+    }
+  }
+
+  /* Handle known answers */
+  while (pkt->answers_left) {
+    struct mdns_answer ans;
+    u8_t rev_v6;
+    int match;
+
+    res = mdns_read_answer(pkt, &ans);
+    if (res != ERR_OK) {
+      LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Failed to parse answer, skipping query packet\n"));
+      goto cleanup;
+    }
+
+    LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Known answer for domain "));
+    mdns_domain_debug_print(&ans.info.domain);
+    LWIP_DEBUGF(MDNS_DEBUG, (" type %d class %d\n", ans.info.type, ans.info.klass));
+
+
+    if (ans.info.type == DNS_RRTYPE_ANY || ans.info.klass == DNS_RRCLASS_ANY) {
+      /* Skip known answers for ANY type & class */
+      continue;
+    }
+
+    rev_v6 = 0;
+    match = reply.host_replies & check_host(pkt->netif, &ans.info, &rev_v6);
+    if (match && (ans.ttl > (mdns->dns_ttl / 2))) {
+      /* The RR in the known answer matches an RR we are planning to send,
+       * and the TTL is less than half gone.
+       * If the payload matches we should not send that answer.
+       */
+      if (ans.info.type == DNS_RRTYPE_PTR) {
+        /* Read domain and compare */
+        struct mdns_domain known_ans, my_ans;
+        u16_t len;
+        len = mdns_readname(pkt->pbuf, ans.rd_offset, &known_ans);
+        res = mdns_build_host_domain(&my_ans, mdns);
+        if (len != MDNS_READNAME_ERROR && res == ERR_OK && mdns_domain_eq(&known_ans, &my_ans)) {
+#if LWIP_IPV4
+          if (match & REPLY_HOST_PTR_V4) {
+              LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: v4 PTR\n"));
+              reply.host_replies &= ~REPLY_HOST_PTR_V4;
+          }
+#endif
+#if LWIP_IPV6
+          if (match & REPLY_HOST_PTR_V6) {
+              LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: v6 PTR\n"));
+              reply.host_reverse_v6_replies &= ~rev_v6;
+              if (reply.host_reverse_v6_replies == 0) {
+                reply.host_replies &= ~REPLY_HOST_PTR_V6;
+              }
+          }
+#endif
+        }
+      } else if (match & REPLY_HOST_A) {
+#if LWIP_IPV4
+        if (ans.rd_length == sizeof(ip4_addr_t) &&
+            pbuf_memcmp(pkt->pbuf, ans.rd_offset, netif_ip4_addr(pkt->netif), ans.rd_length) == 0) {
+          LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: A\n"));
+          reply.host_replies &= ~REPLY_HOST_A;
+        }
+#endif
+      } else if (match & REPLY_HOST_AAAA) {
+#if LWIP_IPV6
+        if (ans.rd_length == sizeof(ip6_addr_t) &&
+            /* TODO this clears all AAAA responses if first addr is set as known */
+            pbuf_memcmp(pkt->pbuf, ans.rd_offset, netif_ip6_addr(pkt->netif, 0), ans.rd_length) == 0) {
+          LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: AAAA\n"));
+          reply.host_replies &= ~REPLY_HOST_AAAA;
+        }
+#endif
+      }
+    }
+
+    for (i = 0; i < MDNS_MAX_SERVICES; ++i) {
+      service = mdns->services[i];
+      if (!service) {
+        continue;
+      }
+      match = reply.serv_replies[i] & check_service(service, &ans.info);
+      if (match && (ans.ttl > (service->dns_ttl / 2))) {
+        /* The RR in the known answer matches an RR we are planning to send,
+         * and the TTL is less than half gone.
+         * If the payload matches we should not send that answer.
+         */
+        if (ans.info.type == DNS_RRTYPE_PTR) {
+          /* Read domain and compare */
+          struct mdns_domain known_ans, my_ans;
+          u16_t len;
+          len = mdns_readname(pkt->pbuf, ans.rd_offset, &known_ans);
+          if (len != MDNS_READNAME_ERROR) {
+            if (match & REPLY_SERVICE_TYPE_PTR) {
+              res = mdns_build_service_domain(&my_ans, service, 0);
+              if (res == ERR_OK && mdns_domain_eq(&known_ans, &my_ans)) {
+                LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: service type PTR\n"));
+                reply.serv_replies[i] &= ~REPLY_SERVICE_TYPE_PTR;
+              }
+            }
+            if (match & REPLY_SERVICE_NAME_PTR) {
+              res = mdns_build_service_domain(&my_ans, service, 1);
+              if (res == ERR_OK && mdns_domain_eq(&known_ans, &my_ans)) {
+                LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: service name PTR\n"));
+                reply.serv_replies[i] &= ~REPLY_SERVICE_NAME_PTR;
+              }
+            }
+          }
+        } else if (match & REPLY_SERVICE_SRV) {
+          /* Read and compare to my SRV record */
+          u16_t field16, len, read_pos;
+          struct mdns_domain known_ans, my_ans;
+          read_pos = ans.rd_offset;
+          do {
+            /* Check priority field */
+            len = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), read_pos);
+            if (len != sizeof(field16) || lwip_ntohs(field16) != SRV_PRIORITY) {
+              break;
+            }
+            read_pos += len;
+            /* Check weight field */
+            len = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), read_pos);
+            if (len != sizeof(field16) || lwip_ntohs(field16) != SRV_WEIGHT) {
+              break;
+            }
+            read_pos += len;
+            /* Check port field */
+            len = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), read_pos);
+            if (len != sizeof(field16) || lwip_ntohs(field16) != service->port) {
+              break;
+            }
+            read_pos += len;
+            /* Check host field */
+            len = mdns_readname(pkt->pbuf, read_pos, &known_ans);
+            mdns_build_host_domain(&my_ans, mdns);
+            if (len == MDNS_READNAME_ERROR || !mdns_domain_eq(&known_ans, &my_ans)) {
+              break;
+            }
+            LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: SRV\n"));
+            reply.serv_replies[i] &= ~REPLY_SERVICE_SRV;
+          } while (0);
+        } else if (match & REPLY_SERVICE_TXT) {
+          mdns_prepare_txtdata(service);
+          if (service->txtdata.length == ans.rd_length &&
+              pbuf_memcmp(pkt->pbuf, ans.rd_offset, service->txtdata.name, ans.rd_length) == 0) {
+            LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: TXT\n"));
+            reply.serv_replies[i] &= ~REPLY_SERVICE_TXT;
+          }
+        }
+      }
+    }
+  }
+
+  mdns_send_outpacket(&reply);
+
+cleanup:
+  if (reply.pbuf) {
+    /* This should only happen if we fail to alloc/write question for legacy query */
+    pbuf_free(reply.pbuf);
+    reply.pbuf = NULL;
+  }
+}
+
+/**
+ * Handle response MDNS packet
+ * Only prints debug for now. Will need more code to do conflict resolution.
+ */
+static void
+mdns_handle_response(struct mdns_packet *pkt)
+{
+  /* Ignore all questions */
+  while (pkt->questions_left) {
+    struct mdns_question q;
+    err_t res;
+
+    res = mdns_read_question(pkt, &q);
+    if (res != ERR_OK) {
+      LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Failed to parse question, skipping response packet\n"));
+      return;
+    }
+  }
+
+  while (pkt->answers_left) {
+    struct mdns_answer ans;
+    err_t res;
+
+    res = mdns_read_answer(pkt, &ans);
+    if (res != ERR_OK) {
+      LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Failed to parse answer, skipping response packet\n"));
+      return;
+    }
+
+    LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Answer for domain "));
+    mdns_domain_debug_print(&ans.info.domain);
+    LWIP_DEBUGF(MDNS_DEBUG, (" type %d class %d\n", ans.info.type, ans.info.klass));
+  }
+}
+
+/**
+ * Receive input function for MDNS packets.
+ * Handles both IPv4 and IPv6 UDP pcbs.
+ */
+static void
+mdns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
+{
+  struct dns_hdr hdr;
+  struct mdns_packet packet;
+  struct netif *recv_netif = ip_current_input_netif();
+  u16_t offset = 0;
+
+  LWIP_UNUSED_ARG(arg);
+  LWIP_UNUSED_ARG(pcb);
+
+  LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Received IPv%d MDNS packet, len %d\n", IP_IS_V6(addr)? 6 : 4, p->tot_len));
+
+  if (NETIF_TO_HOST(recv_netif) == NULL) {
+    /* From netif not configured for MDNS */
+    goto dealloc;
+  }
+
+  if (pbuf_copy_partial(p, &hdr, SIZEOF_DNS_HDR, offset) < SIZEOF_DNS_HDR) {
+    /* Too small */
+    goto dealloc;
+  }
+  offset += SIZEOF_DNS_HDR;
+
+  if (DNS_HDR_GET_OPCODE(&hdr)) {
+    /* Ignore non-standard queries in multicast packets (RFC 6762, section 18.3) */
+    goto dealloc;
+  }
+
+  memset(&packet, 0, sizeof(packet));
+  SMEMCPY(&packet.source_addr, addr, sizeof(packet.source_addr));
+  packet.source_port = port;
+  packet.netif = recv_netif;
+  packet.pbuf = p;
+  packet.parse_offset = offset;
+  packet.tx_id = lwip_ntohs(hdr.id);
+  packet.questions = packet.questions_left = lwip_ntohs(hdr.numquestions);
+  packet.answers = packet.answers_left = lwip_ntohs(hdr.numanswers) + lwip_ntohs(hdr.numauthrr) + lwip_ntohs(hdr.numextrarr);
+
+#if LWIP_IPV6
+  if (IP_IS_V6(ip_current_dest_addr())) {
+    if (!ip_addr_cmp(ip_current_dest_addr(), &v6group)) {
+      packet.recv_unicast = 1;
+    }
+  }
+#endif
+#if LWIP_IPV4
+  if (!IP_IS_V6(ip_current_dest_addr())) {
+    if (!ip_addr_cmp(ip_current_dest_addr(), &v4group)) {
+      packet.recv_unicast = 1;
+    }
+  }
+#endif
+
+  if (hdr.flags1 & DNS_FLAG1_RESPONSE) {
+    mdns_handle_response(&packet);
+  } else {
+    mdns_handle_question(&packet);
+  }
+
+dealloc:
+  pbuf_free(p);
+}
+
+/**
+ * @ingroup mdns
+ * Initiate MDNS responder. Will open UDP sockets on port 5353
+ */
+void
+mdns_resp_init(void)
+{
+  err_t res;
+
+  mdns_pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
+  LWIP_ASSERT("Failed to allocate pcb", mdns_pcb != NULL);
+#if LWIP_MULTICAST_TX_OPTIONS
+  udp_set_multicast_ttl(mdns_pcb, MDNS_TTL);
+#else
+  mdns_pcb->ttl = MDNS_TTL;
+#endif
+  res = udp_bind(mdns_pcb, IP_ANY_TYPE, MDNS_PORT);
+  LWIP_UNUSED_ARG(res); /* in case of LWIP_NOASSERT */
+  LWIP_ASSERT("Failed to bind pcb", res == ERR_OK);
+  udp_recv(mdns_pcb, mdns_recv, NULL);
+
+  mdns_netif_client_id = netif_alloc_client_data_id();
+}
+
+/**
+ * @ingroup mdns
+ * Announce IP settings have changed on netif.
+ * Call this in your callback registered by netif_set_status_callback().
+ * This function may go away in the future when netif supports registering
+ * multiple callback functions.
+ * @param netif The network interface where settings have changed.
+ */
+void
+mdns_resp_netif_settings_changed(struct netif *netif)
+{
+  LWIP_ERROR("mdns_resp_netif_ip_changed: netif != NULL", (netif != NULL), return);
+
+  if (NETIF_TO_HOST(netif) == NULL) {
+    return;
+  }
+
+  /* Announce on IPv6 and IPv4 */
+#if LWIP_IPV6
+   mdns_announce(netif, IP6_ADDR_ANY);
+#endif
+#if LWIP_IPV4
+   mdns_announce(netif, IP4_ADDR_ANY);
+#endif
+}
+
+/**
+ * @ingroup mdns
+ * Activate MDNS responder for a network interface and send announce packets.
+ * @param netif The network interface to activate.
+ * @param hostname Name to use. Queries for &lt;hostname&gt;.local will be answered
+ *                 with the IP addresses of the netif. The hostname will be copied, the
+ *                 given pointer can be on the stack.
+ * @param dns_ttl Validity time in seconds to send out for IP address data in DNS replies
+ * @return ERR_OK if netif was added, an err_t otherwise
+ */
+err_t
+mdns_resp_add_netif(struct netif *netif, const char *hostname, u32_t dns_ttl)
+{
+  err_t res;
+  struct mdns_host* mdns;
+
+  LWIP_ERROR("mdns_resp_add_netif: netif != NULL", (netif != NULL), return ERR_VAL);
+  LWIP_ERROR("mdns_resp_add_netif: Hostname too long", (strlen(hostname) <= MDNS_LABEL_MAXLEN), return ERR_VAL);
+
+  LWIP_ASSERT("mdns_resp_add_netif: Double add", NETIF_TO_HOST(netif) == NULL);
+  mdns = (struct mdns_host *) mem_malloc(sizeof(struct mdns_host));
+  LWIP_ERROR("mdns_resp_add_netif: Alloc failed", (mdns != NULL), return ERR_MEM);
+
+  netif_set_client_data(netif, mdns_netif_client_id, mdns);
+
+  memset(mdns, 0, sizeof(struct mdns_host));
+  MEMCPY(&mdns->name, hostname, LWIP_MIN(MDNS_LABEL_MAXLEN, strlen(hostname)));
+  mdns->dns_ttl = dns_ttl;
+
+  /* Join multicast groups */
+#if LWIP_IPV4
+  res = igmp_joingroup_netif(netif, ip_2_ip4(&v4group));
+  if (res != ERR_OK) {
+    goto cleanup;
+  }
+#endif
+#if LWIP_IPV6
+  res = mld6_joingroup_netif(netif, ip_2_ip6(&v6group));
+  if (res != ERR_OK) {
+    goto cleanup;
+  }
+#endif
+
+  mdns_resp_netif_settings_changed(netif);
+  return ERR_OK;
+
+cleanup:
+  mem_free(mdns);
+  netif_set_client_data(netif, mdns_netif_client_id, NULL);
+  return res;
+}
+
+/**
+ * @ingroup mdns
+ * Stop responding to MDNS queries on this interface, leave multicast groups,
+ * and free the helper structure and any of its services.
+ * @param netif The network interface to remove.
+ * @return ERR_OK if netif was removed, an err_t otherwise
+ */
+err_t
+mdns_resp_remove_netif(struct netif *netif)
+{
+  int i;
+  struct mdns_host* mdns;
+
+  LWIP_ASSERT("mdns_resp_remove_netif: Null pointer", netif);
+  mdns = NETIF_TO_HOST(netif);
+  LWIP_ERROR("mdns_resp_remove_netif: Not an active netif", (mdns != NULL), return ERR_VAL);
+
+  for (i = 0; i < MDNS_MAX_SERVICES; i++) {
+    struct mdns_service *service = mdns->services[i];
+    if (service) {
+      mem_free(service);
+    }
+  }
+
+  /* Leave multicast groups */
+#if LWIP_IPV4
+  igmp_leavegroup_netif(netif, ip_2_ip4(&v4group));
+#endif
+#if LWIP_IPV6
+  mld6_leavegroup_netif(netif, ip_2_ip6(&v6group));
+#endif
+
+  mem_free(mdns);
+  netif_set_client_data(netif, mdns_netif_client_id, NULL);
+  return ERR_OK;
+}
+
+/**
+ * @ingroup mdns
+ * Add a service to the selected network interface.
+ * @param netif The network interface to publish this service on
+ * @param name The name of the service
+ * @param service The service type, like "_http"
+ * @param proto The service protocol, DNSSD_PROTO_TCP for TCP ("_tcp") and DNSSD_PROTO_UDP
+ *              for others ("_udp")
+ * @param port The port the service listens to
+ * @param dns_ttl Validity time in seconds to send out for service data in DNS replies
+ * @param txt_fn Callback to get TXT data. Will be called each time a TXT reply is created to
+ *               allow dynamic replies.
+ * @param txt_data Userdata pointer for txt_fn
+ * @return ERR_OK if the service was added to the netif, an err_t otherwise
+ */
+err_t
+mdns_resp_add_service(struct netif *netif, const char *name, const char *service, enum mdns_sd_proto proto, u16_t port, u32_t dns_ttl, service_get_txt_fn_t txt_fn, void *txt_data)
+{
+  int i;
+  int slot = -1;
+  struct mdns_service *srv;
+  struct mdns_host* mdns;
+
+  LWIP_ASSERT("mdns_resp_add_service: netif != NULL", netif);
+  mdns = NETIF_TO_HOST(netif);
+  LWIP_ERROR("mdns_resp_add_service: Not an mdns netif", (mdns != NULL), return ERR_VAL);
+
+  LWIP_ERROR("mdns_resp_add_service: Name too long", (strlen(name) <= MDNS_LABEL_MAXLEN), return ERR_VAL);
+  LWIP_ERROR("mdns_resp_add_service: Service too long", (strlen(service) <= MDNS_LABEL_MAXLEN), return ERR_VAL);
+  LWIP_ERROR("mdns_resp_add_service: Bad proto (need TCP or UDP)", (proto == DNSSD_PROTO_TCP || proto == DNSSD_PROTO_UDP), return ERR_VAL);
+
+  for (i = 0; i < MDNS_MAX_SERVICES; i++) {
+    if (mdns->services[i] == NULL) {
+      slot = i;
+      break;
+    }
+  }
+  LWIP_ERROR("mdns_resp_add_service: Service list full (increase MDNS_MAX_SERVICES)", (slot >= 0), return ERR_MEM);
+
+  srv = (struct mdns_service*)mem_malloc(sizeof(struct mdns_service));
+  LWIP_ERROR("mdns_resp_add_service: Alloc failed", (srv != NULL), return ERR_MEM);
+
+  memset(srv, 0, sizeof(struct mdns_service));
+
+  MEMCPY(&srv->name, name, LWIP_MIN(MDNS_LABEL_MAXLEN, strlen(name)));
+  MEMCPY(&srv->service, service, LWIP_MIN(MDNS_LABEL_MAXLEN, strlen(service)));
+  srv->txt_fn = txt_fn;
+  srv->txt_userdata = txt_data;
+  srv->proto = (u16_t)proto;
+  srv->port = port;
+  srv->dns_ttl = dns_ttl;
+
+  mdns->services[slot] = srv;
+
+  /* Announce on IPv6 and IPv4 */
+#if LWIP_IPV6
+  mdns_announce(netif, IP6_ADDR_ANY);
+#endif
+#if LWIP_IPV4
+  mdns_announce(netif, IP4_ADDR_ANY);
+#endif
+
+  return ERR_OK;
+}
+
+/**
+ * @ingroup mdns
+ * Call this function from inside the service_get_txt_fn_t callback to add text data.
+ * Buffer for TXT data is 256 bytes, and each field is prefixed with a length byte.
+ * @param service The service provided to the get_txt callback
+ * @param txt String to add to the TXT field.
+ * @param txt_len Length of string
+ * @return ERR_OK if the string was added to the reply, an err_t otherwise
+ */
+err_t
+mdns_resp_add_service_txtitem(struct mdns_service *service, const char *txt, u8_t txt_len)
+{
+  LWIP_ASSERT("mdns_resp_add_service_txtitem: service != NULL", service);
+
+  /* Use a mdns_domain struct to store txt chunks since it is the same encoding */
+  return mdns_domain_add_label(&service->txtdata, txt, txt_len);
+}
+
+#endif /* LWIP_MDNS_RESPONDER */

+ 1373 - 1463
libs/thirdparty/lwip_2.1.2/src/apps/mqtt/mqtt.c → libs/thirdparty/LwIP/src/apps/mqtt/mqtt.c

@@ -1,1463 +1,1373 @@
-/**
- * @file
- * MQTT client
- *
- * @defgroup mqtt MQTT client
- * @ingroup apps
- * @verbinclude mqtt_client.txt
- */
-
-/*
- * Copyright (c) 2016 Erik Andersson <erian747@gmail.com>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack
- *
- * Author: Erik Andersson <erian747@gmail.com>
- *
- *
- * @todo:
- * - Handle large outgoing payloads for PUBLISH messages
- * - Fix restriction of a single topic in each (UN)SUBSCRIBE message (protocol has support for multiple topics)
- * - Add support for legacy MQTT protocol version
- *
- * Please coordinate changes and requests with Erik Andersson
- * Erik Andersson <erian747@gmail.com>
- *
- */
-#include "lwip/apps/mqtt.h"
-#include "lwip/apps/mqtt_priv.h"
-#include "lwip/timeouts.h"
-#include "lwip/ip_addr.h"
-#include "lwip/mem.h"
-#include "lwip/err.h"
-#include "lwip/pbuf.h"
-#include "lwip/altcp.h"
-#include "lwip/altcp_tcp.h"
-#include "lwip/altcp_tls.h"
-#include <string.h>
-
-#if LWIP_TCP && LWIP_CALLBACK_API
-
-/**
- * MQTT_DEBUG: Default is off.
- */
-#if !defined MQTT_DEBUG || defined __DOXYGEN__
-#define MQTT_DEBUG                  LWIP_DBG_OFF
-#endif
-
-#define MQTT_DEBUG_TRACE        (MQTT_DEBUG | LWIP_DBG_TRACE)
-#define MQTT_DEBUG_STATE        (MQTT_DEBUG | LWIP_DBG_STATE)
-#define MQTT_DEBUG_WARN         (MQTT_DEBUG | LWIP_DBG_LEVEL_WARNING)
-#define MQTT_DEBUG_WARN_STATE   (MQTT_DEBUG | LWIP_DBG_LEVEL_WARNING | LWIP_DBG_STATE)
-#define MQTT_DEBUG_SERIOUS      (MQTT_DEBUG | LWIP_DBG_LEVEL_SERIOUS)
-
-
-
-/**
- * MQTT client connection states
- */
-enum {
-  TCP_DISCONNECTED,
-  TCP_CONNECTING,
-  MQTT_CONNECTING,
-  MQTT_CONNECTED
-};
-
-/**
- * MQTT control message types
- */
-enum mqtt_message_type {
-  MQTT_MSG_TYPE_CONNECT = 1,
-  MQTT_MSG_TYPE_CONNACK = 2,
-  MQTT_MSG_TYPE_PUBLISH = 3,
-  MQTT_MSG_TYPE_PUBACK = 4,
-  MQTT_MSG_TYPE_PUBREC = 5,
-  MQTT_MSG_TYPE_PUBREL = 6,
-  MQTT_MSG_TYPE_PUBCOMP = 7,
-  MQTT_MSG_TYPE_SUBSCRIBE = 8,
-  MQTT_MSG_TYPE_SUBACK = 9,
-  MQTT_MSG_TYPE_UNSUBSCRIBE = 10,
-  MQTT_MSG_TYPE_UNSUBACK = 11,
-  MQTT_MSG_TYPE_PINGREQ = 12,
-  MQTT_MSG_TYPE_PINGRESP = 13,
-  MQTT_MSG_TYPE_DISCONNECT = 14
-};
-
-/** Helpers to extract control packet type and qos from first byte in fixed header */
-#define MQTT_CTL_PACKET_TYPE(fixed_hdr_byte0) ((fixed_hdr_byte0 & 0xf0) >> 4)
-#define MQTT_CTL_PACKET_QOS(fixed_hdr_byte0) ((fixed_hdr_byte0 & 0x6) >> 1)
-
-/**
- * MQTT connect flags, only used in CONNECT message
- */
-enum mqtt_connect_flag {
-  MQTT_CONNECT_FLAG_USERNAME = 1 << 7,
-  MQTT_CONNECT_FLAG_PASSWORD = 1 << 6,
-  MQTT_CONNECT_FLAG_WILL_RETAIN = 1 << 5,
-  MQTT_CONNECT_FLAG_WILL = 1 << 2,
-  MQTT_CONNECT_FLAG_CLEAN_SESSION = 1 << 1
-};
-
-
-static void mqtt_cyclic_timer(void *arg);
-
-#if defined(LWIP_DEBUG)
-static const char *const mqtt_message_type_str[15] = {
-  "UNDEFINED",
-  "CONNECT",
-  "CONNACK",
-  "PUBLISH",
-  "PUBACK",
-  "PUBREC",
-  "PUBREL",
-  "PUBCOMP",
-  "SUBSCRIBE",
-  "SUBACK",
-  "UNSUBSCRIBE",
-  "UNSUBACK",
-  "PINGREQ",
-  "PINGRESP",
-  "DISCONNECT"
-};
-
-/**
- * Message type value to string
- * @param msg_type see enum mqtt_message_type
- *
- * @return Control message type text string
- */
-static const char *
-mqtt_msg_type_to_str(u8_t msg_type)
-{
-  if (msg_type >= LWIP_ARRAYSIZE(mqtt_message_type_str)) {
-    msg_type = 0;
-  }
-  return mqtt_message_type_str[msg_type];
-}
-
-#endif
-
-
-/**
- * Generate MQTT packet identifier
- * @param client MQTT client
- * @return New packet identifier, range 1 to 65535
- */
-static u16_t
-msg_generate_packet_id(mqtt_client_t *client)
-{
-  client->pkt_id_seq++;
-  if (client->pkt_id_seq == 0) {
-    client->pkt_id_seq++;
-  }
-  return client->pkt_id_seq;
-}
-
-/*--------------------------------------------------------------------------------------------------------------------- */
-/* Output ring buffer */
-
-/** Add single item to ring buffer */
-static void
-mqtt_ringbuf_put(struct mqtt_ringbuf_t *rb, u8_t item)
-{
-  rb->buf[rb->put] = item;
-  rb->put++;
-  if (rb->put >= MQTT_OUTPUT_RINGBUF_SIZE) {
-    rb->put = 0;
-  }
-}
-
-/** Return pointer to ring buffer get position */
-static u8_t *
-mqtt_ringbuf_get_ptr(struct mqtt_ringbuf_t *rb)
-{
-  return &rb->buf[rb->get];
-}
-
-static void
-mqtt_ringbuf_advance_get_idx(struct mqtt_ringbuf_t *rb, u16_t len)
-{
-  LWIP_ASSERT("mqtt_ringbuf_advance_get_idx: len < MQTT_OUTPUT_RINGBUF_SIZE", len < MQTT_OUTPUT_RINGBUF_SIZE);
-
-  rb->get += len;
-  if (rb->get >= MQTT_OUTPUT_RINGBUF_SIZE) {
-    rb->get = rb->get - MQTT_OUTPUT_RINGBUF_SIZE;
-  }
-}
-
-/** Return number of bytes in ring buffer */
-static u16_t
-mqtt_ringbuf_len(struct mqtt_ringbuf_t *rb)
-{
-  u32_t len = rb->put - rb->get;
-  if (len > 0xFFFF) {
-    len += MQTT_OUTPUT_RINGBUF_SIZE;
-  }
-  return (u16_t)len;
-}
-
-/** Return number of bytes free in ring buffer */
-#define mqtt_ringbuf_free(rb) (MQTT_OUTPUT_RINGBUF_SIZE - mqtt_ringbuf_len(rb))
-
-/** Return number of bytes possible to read without wrapping around */
-#define mqtt_ringbuf_linear_read_length(rb) LWIP_MIN(mqtt_ringbuf_len(rb), (MQTT_OUTPUT_RINGBUF_SIZE - (rb)->get))
-
-/**
- * Try send as many bytes as possible from output ring buffer
- * @param rb Output ring buffer
- * @param tpcb TCP connection handle
- */
-static void
-mqtt_output_send(struct mqtt_ringbuf_t *rb, struct altcp_pcb *tpcb)
-{
-  err_t err;
-  u8_t wrap = 0;
-  u16_t ringbuf_lin_len = mqtt_ringbuf_linear_read_length(rb);
-  u16_t send_len = altcp_sndbuf(tpcb);
-  LWIP_ASSERT("mqtt_output_send: tpcb != NULL", tpcb != NULL);
-
-  if (send_len == 0 || ringbuf_lin_len == 0) {
-    return;
-  }
-
-  LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_output_send: tcp_sndbuf: %d bytes, ringbuf_linear_available: %d, get %d, put %d\n",
-                                 send_len, ringbuf_lin_len, rb->get, rb->put));
-
-  if (send_len > ringbuf_lin_len) {
-    /* Space in TCP output buffer is larger than available in ring buffer linear portion */
-    send_len = ringbuf_lin_len;
-    /* Wrap around if more data in ring buffer after linear portion */
-    wrap = (mqtt_ringbuf_len(rb) > ringbuf_lin_len);
-  }
-  err = altcp_write(tpcb, mqtt_ringbuf_get_ptr(rb), send_len, TCP_WRITE_FLAG_COPY | (wrap ? TCP_WRITE_FLAG_MORE : 0));
-  if ((err == ERR_OK) && wrap) {
-    mqtt_ringbuf_advance_get_idx(rb, send_len);
-    /* Use the lesser one of ring buffer linear length and TCP send buffer size */
-    send_len = LWIP_MIN(altcp_sndbuf(tpcb), mqtt_ringbuf_linear_read_length(rb));
-    err = altcp_write(tpcb, mqtt_ringbuf_get_ptr(rb), send_len, TCP_WRITE_FLAG_COPY);
-  }
-
-  if (err == ERR_OK) {
-    mqtt_ringbuf_advance_get_idx(rb, send_len);
-    /* Flush */
-    altcp_output(tpcb);
-  } else {
-    LWIP_DEBUGF(MQTT_DEBUG_WARN, ("mqtt_output_send: Send failed with err %d (\"%s\")\n", err, lwip_strerr(err)));
-  }
-}
-
-
-
-/*--------------------------------------------------------------------------------------------------------------------- */
-/* Request queue */
-
-/**
- * Create request item
- * @param r_objs Pointer to request objects
- * @param r_objs_len Number of array entries
- * @param pkt_id Packet identifier of request
- * @param cb Packet callback to call when requests lifetime ends
- * @param arg Parameter following callback
- * @return Request or NULL if failed to create
- */
-static struct mqtt_request_t *
-mqtt_create_request(struct mqtt_request_t *r_objs, size_t r_objs_len, u16_t pkt_id, mqtt_request_cb_t cb, void *arg)
-{
-  struct mqtt_request_t *r = NULL;
-  u8_t n;
-  LWIP_ASSERT("mqtt_create_request: r_objs != NULL", r_objs != NULL);
-  for (n = 0; n < r_objs_len; n++) {
-    /* Item point to itself if not in use */
-    if (r_objs[n].next == &r_objs[n]) {
-      r = &r_objs[n];
-      r->next = NULL;
-      r->cb = cb;
-      r->arg = arg;
-      r->pkt_id = pkt_id;
-      break;
-    }
-  }
-  return r;
-}
-
-
-/**
- * Append request to pending request queue
- * @param tail Pointer to request queue tail pointer
- * @param r Request to append
- */
-static void
-mqtt_append_request(struct mqtt_request_t **tail, struct mqtt_request_t *r)
-{
-  struct mqtt_request_t *head = NULL;
-  s16_t time_before = 0;
-  struct mqtt_request_t *iter;
-
-  LWIP_ASSERT("mqtt_append_request: tail != NULL", tail != NULL);
-
-  /* Iterate trough queue to find head, and count total timeout time */
-  for (iter = *tail; iter != NULL; iter = iter->next) {
-    time_before += iter->timeout_diff;
-    head = iter;
-  }
-
-  LWIP_ASSERT("mqtt_append_request: time_before <= MQTT_REQ_TIMEOUT", time_before <= MQTT_REQ_TIMEOUT);
-  r->timeout_diff = MQTT_REQ_TIMEOUT - time_before;
-  if (head == NULL) {
-    *tail = r;
-  } else {
-    head->next = r;
-  }
-}
-
-
-/**
- * Delete request item
- * @param r Request item to delete
- */
-static void
-mqtt_delete_request(struct mqtt_request_t *r)
-{
-  if (r != NULL) {
-    r->next = r;
-  }
-}
-
-/**
- * Remove a request item with a specific packet identifier from request queue
- * @param tail Pointer to request queue tail pointer
- * @param pkt_id Packet identifier of request to take
- * @return Request item if found, NULL if not
- */
-static struct mqtt_request_t *
-mqtt_take_request(struct mqtt_request_t **tail, u16_t pkt_id)
-{
-  struct mqtt_request_t *iter = NULL, *prev = NULL;
-  LWIP_ASSERT("mqtt_take_request: tail != NULL", tail != NULL);
-  /* Search all request for pkt_id */
-  for (iter = *tail; iter != NULL; iter = iter->next) {
-    if (iter->pkt_id == pkt_id) {
-      break;
-    }
-    prev = iter;
-  }
-
-  /* If request was found */
-  if (iter != NULL) {
-    /* unchain */
-    if (prev == NULL) {
-      *tail = iter->next;
-    } else {
-      prev->next = iter->next;
-    }
-    /* If exists, add remaining timeout time for the request to next */
-    if (iter->next != NULL) {
-      iter->next->timeout_diff += iter->timeout_diff;
-    }
-    iter->next = NULL;
-  }
-  return iter;
-}
-
-/**
- * Handle requests timeout
- * @param tail Pointer to request queue tail pointer
- * @param t Time since last call in seconds
- */
-static void
-mqtt_request_time_elapsed(struct mqtt_request_t **tail, u8_t t)
-{
-  struct mqtt_request_t *r;
-  LWIP_ASSERT("mqtt_request_time_elapsed: tail != NULL", tail != NULL);
-  r = *tail;
-  while (t > 0 && r != NULL) {
-    if (t >= r->timeout_diff) {
-      t -= (u8_t)r->timeout_diff;
-      /* Unchain */
-      *tail = r->next;
-      /* Notify upper layer about timeout */
-      if (r->cb != NULL) {
-        r->cb(r->arg, ERR_TIMEOUT);
-      }
-      mqtt_delete_request(r);
-      /* Tail might be be modified in callback, so re-read it in every iteration */
-      r = *(struct mqtt_request_t *const volatile *)tail;
-    } else {
-      r->timeout_diff -= t;
-      t = 0;
-    }
-  }
-}
-
-/**
- * Free all request items
- * @param tail Pointer to request queue tail pointer
- */
-static void
-mqtt_clear_requests(struct mqtt_request_t **tail)
-{
-  struct mqtt_request_t *iter, *next;
-  LWIP_ASSERT("mqtt_clear_requests: tail != NULL", tail != NULL);
-  for (iter = *tail; iter != NULL; iter = next) {
-    next = iter->next;
-    mqtt_delete_request(iter);
-  }
-  *tail = NULL;
-}
-/**
- * Initialize all request items
- * @param r_objs Pointer to request objects
- * @param r_objs_len Number of array entries
- */
-static void
-mqtt_init_requests(struct mqtt_request_t *r_objs, size_t r_objs_len)
-{
-  u8_t n;
-  LWIP_ASSERT("mqtt_init_requests: r_objs != NULL", r_objs != NULL);
-  for (n = 0; n < r_objs_len; n++) {
-    /* Item pointing to itself indicates unused */
-    r_objs[n].next = &r_objs[n];
-  }
-}
-
-/*--------------------------------------------------------------------------------------------------------------------- */
-/* Output message build helpers */
-
-
-static void
-mqtt_output_append_u8(struct mqtt_ringbuf_t *rb, u8_t value)
-{
-  mqtt_ringbuf_put(rb, value);
-}
-
-static
-void mqtt_output_append_u16(struct mqtt_ringbuf_t *rb, u16_t value)
-{
-  mqtt_ringbuf_put(rb, value >> 8);
-  mqtt_ringbuf_put(rb, value & 0xff);
-}
-
-static void
-mqtt_output_append_buf(struct mqtt_ringbuf_t *rb, const void *data, u16_t length)
-{
-  u16_t n;
-  for (n = 0; n < length; n++) {
-    mqtt_ringbuf_put(rb, ((const u8_t *)data)[n]);
-  }
-}
-
-static void
-mqtt_output_append_string(struct mqtt_ringbuf_t *rb, const char *str, u16_t length)
-{
-  u16_t n;
-  mqtt_ringbuf_put(rb, length >> 8);
-  mqtt_ringbuf_put(rb, length & 0xff);
-  for (n = 0; n < length; n++) {
-    mqtt_ringbuf_put(rb, str[n]);
-  }
-}
-
-/**
- * Append fixed header
- * @param rb Output ring buffer
- * @param msg_type see enum mqtt_message_type
- * @param fdup MQTT DUP flag
- * @param fqos MQTT QoS field
- * @param fretain MQTT retain flag
- * @param r_length Remaining length after fixed header
- */
-
-static void
-mqtt_output_append_fixed_header(struct mqtt_ringbuf_t *rb, u8_t msg_type, u8_t fdup,
-                                u8_t fqos, u8_t fretain, u16_t r_length)
-{
-  /* Start with control byte */
-  mqtt_output_append_u8(rb, (((msg_type & 0x0f) << 4) | ((fdup & 1) << 3) | ((fqos & 3) << 1) | (fretain & 1)));
-  /* Encode remaining length field */
-  do {
-    mqtt_output_append_u8(rb, (r_length & 0x7f) | (r_length >= 128 ? 0x80 : 0));
-    r_length >>= 7;
-  } while (r_length > 0);
-}
-
-
-/**
- * Check output buffer space
- * @param rb Output ring buffer
- * @param r_length Remaining length after fixed header
- * @return 1 if message will fit, 0 if not enough buffer space
- */
-static u8_t
-mqtt_output_check_space(struct mqtt_ringbuf_t *rb, u16_t r_length)
-{
-  /* Start with length of type byte + remaining length */
-  u16_t total_len = 1 + r_length;
-
-  LWIP_ASSERT("mqtt_output_check_space: rb != NULL", rb != NULL);
-
-  /* Calculate number of required bytes to contain the remaining bytes field and add to total*/
-  do {
-    total_len++;
-    r_length >>= 7;
-  } while (r_length > 0);
-
-  return (total_len <= mqtt_ringbuf_free(rb));
-}
-
-
-/**
- * Close connection to server
- * @param client MQTT client
- * @param reason Reason for disconnection
- */
-static void
-mqtt_close(mqtt_client_t *client, mqtt_connection_status_t reason)
-{
-  LWIP_ASSERT("mqtt_close: client != NULL", client != NULL);
-
-  /* Bring down TCP connection if not already done */
-  if (client->conn != NULL) {
-    err_t res;
-    altcp_recv(client->conn, NULL);
-    altcp_err(client->conn,  NULL);
-    altcp_sent(client->conn, NULL);
-    res = altcp_close(client->conn);
-    if (res != ERR_OK) {
-      altcp_abort(client->conn);
-      LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_close: Close err=%s\n", lwip_strerr(res)));
-    }
-    client->conn = NULL;
-  }
-
-  /* Remove all pending requests */
-  mqtt_clear_requests(&client->pend_req_queue);
-  /* Stop cyclic timer */
-  sys_untimeout(mqtt_cyclic_timer, client);
-
-  /* Notify upper layer of disconnection if changed state */
-  if (client->conn_state != TCP_DISCONNECTED) {
-
-    client->conn_state = TCP_DISCONNECTED;
-    if (client->connect_cb != NULL) {
-      client->connect_cb(client, client->connect_arg, reason);
-    }
-  }
-}
-
-
-/**
- * Interval timer, called every MQTT_CYCLIC_TIMER_INTERVAL seconds in MQTT_CONNECTING and MQTT_CONNECTED states
- * @param arg MQTT client
- */
-static void
-mqtt_cyclic_timer(void *arg)
-{
-  u8_t restart_timer = 1;
-  mqtt_client_t *client = (mqtt_client_t *)arg;
-  LWIP_ASSERT("mqtt_cyclic_timer: client != NULL", client != NULL);
-
-  if (client->conn_state == MQTT_CONNECTING) {
-    client->cyclic_tick++;
-    if ((client->cyclic_tick * MQTT_CYCLIC_TIMER_INTERVAL) >= MQTT_CONNECT_TIMOUT) {
-      LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_cyclic_timer: CONNECT attempt to server timed out\n"));
-      /* Disconnect TCP */
-      mqtt_close(client, MQTT_CONNECT_TIMEOUT);
-      restart_timer = 0;
-    }
-  } else if (client->conn_state == MQTT_CONNECTED) {
-    /* Handle timeout for pending requests */
-    mqtt_request_time_elapsed(&client->pend_req_queue, MQTT_CYCLIC_TIMER_INTERVAL);
-
-    /* keep_alive > 0 means keep alive functionality shall be used */
-    if (client->keep_alive > 0) {
-
-      client->server_watchdog++;
-      /* If reception from server has been idle for 1.5*keep_alive time, server is considered unresponsive */
-      if ((client->server_watchdog * MQTT_CYCLIC_TIMER_INTERVAL) > (client->keep_alive + client->keep_alive / 2)) {
-        LWIP_DEBUGF(MQTT_DEBUG_WARN, ("mqtt_cyclic_timer: Server incoming keep-alive timeout\n"));
-        mqtt_close(client, MQTT_CONNECT_TIMEOUT);
-        restart_timer = 0;
-      }
-
-      /* If time for a keep alive message to be sent, transmission has been idle for keep_alive time */
-      if ((client->cyclic_tick * MQTT_CYCLIC_TIMER_INTERVAL) >= client->keep_alive) {
-        LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_cyclic_timer: Sending keep-alive message to server\n"));
-        if (mqtt_output_check_space(&client->output, 0) != 0) {
-          mqtt_output_append_fixed_header(&client->output, MQTT_MSG_TYPE_PINGREQ, 0, 0, 0, 0);
-          client->cyclic_tick = 0;
-        }
-      } else {
-        client->cyclic_tick++;
-      }
-    }
-  } else {
-    LWIP_DEBUGF(MQTT_DEBUG_WARN, ("mqtt_cyclic_timer: Timer should not be running in state %d\n", client->conn_state));
-    restart_timer = 0;
-  }
-  if (restart_timer) {
-    sys_timeout(MQTT_CYCLIC_TIMER_INTERVAL * 1000, mqtt_cyclic_timer, arg);
-  }
-}
-
-
-/**
- * Send PUBACK, PUBREC or PUBREL response message
- * @param client MQTT client
- * @param msg PUBACK, PUBREC or PUBREL
- * @param pkt_id Packet identifier
- * @param qos QoS value
- * @return ERR_OK if successful, ERR_MEM if out of memory
- */
-static err_t
-pub_ack_rec_rel_response(mqtt_client_t *client, u8_t msg, u16_t pkt_id, u8_t qos)
-{
-  err_t err = ERR_OK;
-  if (mqtt_output_check_space(&client->output, 2)) {
-    mqtt_output_append_fixed_header(&client->output, msg, 0, qos, 0, 2);
-    mqtt_output_append_u16(&client->output, pkt_id);
-    mqtt_output_send(&client->output, client->conn);
-  } else {
-    LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("pub_ack_rec_rel_response: OOM creating response: %s with pkt_id: %d\n",
-                                   mqtt_msg_type_to_str(msg), pkt_id));
-    err = ERR_MEM;
-  }
-  return err;
-}
-
-/**
- * Subscribe response from server
- * @param r Matching request
- * @param result Result code from server
- */
-static void
-mqtt_incomming_suback(struct mqtt_request_t *r, u8_t result)
-{
-  if (r->cb != NULL) {
-    r->cb(r->arg, result < 3 ? ERR_OK : ERR_ABRT);
-  }
-}
-
-
-/**
- * Complete MQTT message received or buffer full
- * @param client MQTT client
- * @param fixed_hdr_idx header index
- * @param length length received part
- * @param remaining_length Remaining length of complete message
- */
-static mqtt_connection_status_t
-mqtt_message_received(mqtt_client_t *client, u8_t fixed_hdr_idx, u16_t length, u32_t remaining_length)
-{
-  mqtt_connection_status_t res = MQTT_CONNECT_ACCEPTED;
-
-  u8_t *var_hdr_payload = client->rx_buffer + fixed_hdr_idx;
-  size_t var_hdr_payload_bufsize = sizeof(client->rx_buffer) - fixed_hdr_idx;
-
-  /* Control packet type */
-  u8_t pkt_type = MQTT_CTL_PACKET_TYPE(client->rx_buffer[0]);
-  u16_t pkt_id = 0;
-
-  LWIP_ASSERT("client->msg_idx < MQTT_VAR_HEADER_BUFFER_LEN", client->msg_idx < MQTT_VAR_HEADER_BUFFER_LEN);
-  LWIP_ASSERT("fixed_hdr_idx <= client->msg_idx", fixed_hdr_idx <= client->msg_idx);
-  LWIP_ERROR("buffer length mismatch", fixed_hdr_idx + length <= MQTT_VAR_HEADER_BUFFER_LEN,
-             return MQTT_CONNECT_DISCONNECTED);
-
-  if (pkt_type == MQTT_MSG_TYPE_CONNACK) {
-    if (client->conn_state == MQTT_CONNECTING) {
-      if (length < 2) {
-        LWIP_DEBUGF(MQTT_DEBUG_WARN,( "mqtt_message_received: Received short CONNACK message\n"));
-        goto out_disconnect;
-      }
-      /* Get result code from CONNACK */
-      res = (mqtt_connection_status_t)var_hdr_payload[1];
-      LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_message_received: Connect response code %d\n", res));
-      if (res == MQTT_CONNECT_ACCEPTED) {
-        /* Reset cyclic_tick when changing to connected state */
-        client->cyclic_tick = 0;
-        client->conn_state = MQTT_CONNECTED;
-        /* Notify upper layer */
-        if (client->connect_cb != 0) {
-          client->connect_cb(client, client->connect_arg, res);
-        }
-      }
-    } else {
-      LWIP_DEBUGF(MQTT_DEBUG_WARN, ("mqtt_message_received: Received CONNACK in connected state\n"));
-    }
-  } else if (pkt_type == MQTT_MSG_TYPE_PINGRESP) {
-    LWIP_DEBUGF(MQTT_DEBUG_TRACE, ( "mqtt_message_received: Received PINGRESP from server\n"));
-
-  } else if (pkt_type == MQTT_MSG_TYPE_PUBLISH) {
-    u16_t payload_offset = 0;
-    u16_t payload_length = length;
-    u8_t qos = MQTT_CTL_PACKET_QOS(client->rx_buffer[0]);
-
-    if (client->msg_idx <= MQTT_VAR_HEADER_BUFFER_LEN) {
-      /* Should have topic and pkt id*/
-      u8_t *topic;
-      u16_t after_topic;
-      u8_t bkp;
-      u16_t topic_len;
-      u16_t qos_len = (qos ? 2U : 0U);
-      if (length < 2 + qos_len) {
-        LWIP_DEBUGF(MQTT_DEBUG_WARN,( "mqtt_message_received: Received short PUBLISH packet\n"));
-        goto out_disconnect;
-      }
-      topic_len = var_hdr_payload[0];
-      topic_len = (topic_len << 8) + (u16_t)(var_hdr_payload[1]);
-      if ((topic_len > length - (2 + qos_len)) ||
-          (topic_len > var_hdr_payload_bufsize - (2 + qos_len))) {
-        LWIP_DEBUGF(MQTT_DEBUG_WARN,( "mqtt_message_received: Received short PUBLISH packet (topic)\n"));
-        goto out_disconnect;
-      }
-
-      topic = var_hdr_payload + 2;
-      after_topic = 2 + topic_len;
-      /* Check buffer length, add one byte even for QoS 0 so that zero termination will fit */
-      if ((after_topic + (qos ? 2U : 1U)) > var_hdr_payload_bufsize) {
-        LWIP_DEBUGF(MQTT_DEBUG_WARN, ("mqtt_message_received: Receive buffer can not fit topic + pkt_id\n"));
-        goto out_disconnect;
-      }
-
-      /* id for QoS 1 and 2 */
-      if (qos > 0) {
-        if (length < after_topic + 2U) {
-          LWIP_DEBUGF(MQTT_DEBUG_WARN,( "mqtt_message_received: Received short PUBLISH packet (after_topic)\n"));
-          goto out_disconnect;
-        }
-        client->inpub_pkt_id = ((u16_t)var_hdr_payload[after_topic] << 8) + (u16_t)var_hdr_payload[after_topic + 1];
-        after_topic += 2;
-      } else {
-        client->inpub_pkt_id = 0;
-      }
-      /* Take backup of byte after topic */
-      bkp = topic[topic_len];
-      /* Zero terminate string */
-      topic[topic_len] = 0;
-      /* Payload data remaining in receive buffer */
-      payload_length = length - after_topic;
-      payload_offset = after_topic;
-
-      LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_incomming_publish: Received message with QoS %d at topic: %s, payload length %"U32_F"\n",
-                                     qos, topic, remaining_length + payload_length));
-      if (client->pub_cb != NULL) {
-        client->pub_cb(client->inpub_arg, (const char *)topic, remaining_length + payload_length);
-      }
-      /* Restore byte after topic */
-      topic[topic_len] = bkp;
-    }
-    if (payload_length > 0 || remaining_length == 0) {
-      if (length < (size_t)(payload_offset + payload_length)) {
-        LWIP_DEBUGF(MQTT_DEBUG_WARN,( "mqtt_message_received: Received short packet (payload)\n"));
-        goto out_disconnect;
-      }
-      client->data_cb(client->inpub_arg, var_hdr_payload + payload_offset, payload_length, remaining_length == 0 ? MQTT_DATA_FLAG_LAST : 0);
-      /* Reply if QoS > 0 */
-      if (remaining_length == 0 && qos > 0) {
-        /* Send PUBACK for QoS 1 or PUBREC for QoS 2 */
-        u8_t resp_msg = (qos == 1) ? MQTT_MSG_TYPE_PUBACK : MQTT_MSG_TYPE_PUBREC;
-        LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_incomming_publish: Sending publish response: %s with pkt_id: %d\n",
-                                       mqtt_msg_type_to_str(resp_msg), client->inpub_pkt_id));
-        pub_ack_rec_rel_response(client, resp_msg, client->inpub_pkt_id, 0);
-      }
-    }
-  } else {
-    /* Get packet identifier */
-    pkt_id = (u16_t)var_hdr_payload[0] << 8;
-    pkt_id |= (u16_t)var_hdr_payload[1];
-    if (pkt_id == 0) {
-      LWIP_DEBUGF(MQTT_DEBUG_WARN, ("mqtt_message_received: Got message with illegal packet identifier: 0\n"));
-      goto out_disconnect;
-    }
-    if (pkt_type == MQTT_MSG_TYPE_PUBREC) {
-      LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_message_received: PUBREC, sending PUBREL with pkt_id: %d\n", pkt_id));
-      pub_ack_rec_rel_response(client, MQTT_MSG_TYPE_PUBREL, pkt_id, 1);
-
-    } else if (pkt_type == MQTT_MSG_TYPE_PUBREL) {
-      LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_message_received: PUBREL, sending PUBCOMP response with pkt_id: %d\n", pkt_id));
-      pub_ack_rec_rel_response(client, MQTT_MSG_TYPE_PUBCOMP, pkt_id, 0);
-
-    } else if (pkt_type == MQTT_MSG_TYPE_SUBACK || pkt_type == MQTT_MSG_TYPE_UNSUBACK ||
-               pkt_type == MQTT_MSG_TYPE_PUBCOMP || pkt_type == MQTT_MSG_TYPE_PUBACK) {
-      struct mqtt_request_t *r = mqtt_take_request(&client->pend_req_queue, pkt_id);
-      if (r != NULL) {
-        LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_message_received: %s response with id %d\n", mqtt_msg_type_to_str(pkt_type), pkt_id));
-        if (pkt_type == MQTT_MSG_TYPE_SUBACK) {
-          if (length < 3) {
-            LWIP_DEBUGF(MQTT_DEBUG_WARN, ("mqtt_message_received: To small SUBACK packet\n"));
-            goto out_disconnect;
-          } else {
-            mqtt_incomming_suback(r, var_hdr_payload[2]);
-          }
-        } else if (r->cb != NULL) {
-          r->cb(r->arg, ERR_OK);
-        }
-        mqtt_delete_request(r);
-      } else {
-        LWIP_DEBUGF(MQTT_DEBUG_WARN, ( "mqtt_message_received: Received %s reply, with wrong pkt_id: %d\n", mqtt_msg_type_to_str(pkt_type), pkt_id));
-      }
-    } else {
-      LWIP_DEBUGF(MQTT_DEBUG_WARN, ( "mqtt_message_received: Received unknown message type: %d\n", pkt_type));
-      goto out_disconnect;
-    }
-  }
-  return res;
-out_disconnect:
-  return MQTT_CONNECT_DISCONNECTED;
-}
-
-
-/**
- * MQTT incoming message parser
- * @param client MQTT client
- * @param p PBUF chain of received data
- * @return Connection status
- */
-static mqtt_connection_status_t
-mqtt_parse_incoming(mqtt_client_t *client, struct pbuf *p)
-{
-  u16_t in_offset = 0;
-  u32_t msg_rem_len = 0;
-  u8_t fixed_hdr_idx = 0;
-  u8_t b = 0;
-
-  while (p->tot_len > in_offset) {
-    /* We ALWAYS parse the header here first. Even if the header was not
-       included in this segment, we re-parse it here by buffering it in
-       client->rx_buffer. client->msg_idx keeps track of this. */
-    if ((fixed_hdr_idx < 2) || ((b & 0x80) != 0)) {
-
-      if (fixed_hdr_idx < client->msg_idx) {
-        /* parse header from old pbuf (buffered in client->rx_buffer) */
-        b = client->rx_buffer[fixed_hdr_idx];
-      } else {
-        /* parse header from this pbuf and save it in client->rx_buffer in case
-           it comes in segmented */
-        b = pbuf_get_at(p, in_offset++);
-        client->rx_buffer[client->msg_idx++] = b;
-      }
-      fixed_hdr_idx++;
-
-      if (fixed_hdr_idx >= 2) {
-        /* fixed header contains at least 2 bytes but can contain more, depending on
-           'remaining length'. All bytes but the last of this have 0x80 set to
-           indicate more bytes are coming. */
-        msg_rem_len |= (u32_t)(b & 0x7f) << ((fixed_hdr_idx - 2) * 7);
-        if ((b & 0x80) == 0) {
-          /* fixed header is done */
-          LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_parse_incoming: Remaining length after fixed header: %"U32_F"\n", msg_rem_len));
-          if (msg_rem_len == 0) {
-            /* Complete message with no extra headers of payload received */
-            mqtt_message_received(client, fixed_hdr_idx, 0, 0);
-            client->msg_idx = 0;
-            fixed_hdr_idx = 0;
-          } else {
-            /* Bytes remaining in message (changes remaining length if this is
-               not the first segment of this message) */
-            msg_rem_len = (msg_rem_len + fixed_hdr_idx) - client->msg_idx;
-          }
-        }
-      }
-    } else {
-      /* Fixed header has been parsed, parse variable header */
-      u16_t cpy_len, cpy_start, buffer_space;
-
-      cpy_start = (client->msg_idx - fixed_hdr_idx) % (MQTT_VAR_HEADER_BUFFER_LEN - fixed_hdr_idx) + fixed_hdr_idx;
-
-      /* Allow to copy the lesser one of available length in input data or bytes remaining in message */
-      cpy_len = (u16_t)LWIP_MIN((u16_t)(p->tot_len - in_offset), msg_rem_len);
-
-      /* Limit to available space in buffer */
-      buffer_space = MQTT_VAR_HEADER_BUFFER_LEN - cpy_start;
-      if (cpy_len > buffer_space) {
-        cpy_len = buffer_space;
-      }
-      pbuf_copy_partial(p, client->rx_buffer + cpy_start, cpy_len, in_offset);
-
-      /* Advance get and put indexes  */
-      client->msg_idx += cpy_len;
-      in_offset += cpy_len;
-      msg_rem_len -= cpy_len;
-
-      LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_parse_incoming: msg_idx: %"U32_F", cpy_len: %"U16_F", remaining %"U32_F"\n", client->msg_idx, cpy_len, msg_rem_len));
-      if ((msg_rem_len == 0) || (cpy_len == buffer_space)) {
-        /* Whole message received or buffer is full */
-        mqtt_connection_status_t res = mqtt_message_received(client, fixed_hdr_idx, (cpy_start + cpy_len) - fixed_hdr_idx, msg_rem_len);
-        if (res != MQTT_CONNECT_ACCEPTED) {
-          return res;
-        }
-        if (msg_rem_len == 0) {
-          /* Reset parser state */
-          client->msg_idx = 0;
-          /* msg_tot_len = 0; */
-          fixed_hdr_idx = 0;
-        }
-      }
-    }
-  }
-  return MQTT_CONNECT_ACCEPTED;
-}
-
-
-/**
- * TCP received callback function. @see tcp_recv_fn
- * @param arg MQTT client
- * @param p PBUF chain of received data
- * @param err Passed as return value if not ERR_OK
- * @return ERR_OK or err passed into callback
- */
-static err_t
-mqtt_tcp_recv_cb(void *arg, struct altcp_pcb *pcb, struct pbuf *p, err_t err)
-{
-  mqtt_client_t *client = (mqtt_client_t *)arg;
-  LWIP_ASSERT("mqtt_tcp_recv_cb: client != NULL", client != NULL);
-  LWIP_ASSERT("mqtt_tcp_recv_cb: client->conn == pcb", client->conn == pcb);
-
-  if (p == NULL) {
-    LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_tcp_recv_cb: Recv pbuf=NULL, remote has closed connection\n"));
-    mqtt_close(client, MQTT_CONNECT_DISCONNECTED);
-  } else {
-    mqtt_connection_status_t res;
-    if (err != ERR_OK) {
-      LWIP_DEBUGF(MQTT_DEBUG_WARN, ("mqtt_tcp_recv_cb: Recv err=%d\n", err));
-      pbuf_free(p);
-      return err;
-    }
-
-    /* Tell remote that data has been received */
-    altcp_recved(pcb, p->tot_len);
-    res = mqtt_parse_incoming(client, p);
-    pbuf_free(p);
-
-    if (res != MQTT_CONNECT_ACCEPTED) {
-      mqtt_close(client, res);
-    }
-    /* If keep alive functionality is used */
-    if (client->keep_alive != 0) {
-      /* Reset server alive watchdog */
-      client->server_watchdog = 0;
-    }
-
-  }
-  return ERR_OK;
-}
-
-
-/**
- * TCP data sent callback function. @see tcp_sent_fn
- * @param arg MQTT client
- * @param tpcb TCP connection handle
- * @param len Number of bytes sent
- * @return ERR_OK
- */
-static err_t
-mqtt_tcp_sent_cb(void *arg, struct altcp_pcb *tpcb, u16_t len)
-{
-  mqtt_client_t *client = (mqtt_client_t *)arg;
-
-  LWIP_UNUSED_ARG(tpcb);
-  LWIP_UNUSED_ARG(len);
-
-  if (client->conn_state == MQTT_CONNECTED) {
-    struct mqtt_request_t *r;
-
-    /* Reset keep-alive send timer and server watchdog */
-    client->cyclic_tick = 0;
-    client->server_watchdog = 0;
-    /* QoS 0 publish has no response from server, so call its callbacks here */
-    while ((r = mqtt_take_request(&client->pend_req_queue, 0)) != NULL) {
-      LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_tcp_sent_cb: Calling QoS 0 publish complete callback\n"));
-      if (r->cb != NULL) {
-        r->cb(r->arg, ERR_OK);
-      }
-      mqtt_delete_request(r);
-    }
-    /* Try send any remaining buffers from output queue */
-    mqtt_output_send(&client->output, client->conn);
-  }
-  return ERR_OK;
-}
-
-/**
- * TCP error callback function. @see tcp_err_fn
- * @param arg MQTT client
- * @param err Error encountered
- */
-static void
-mqtt_tcp_err_cb(void *arg, err_t err)
-{
-  mqtt_client_t *client = (mqtt_client_t *)arg;
-  LWIP_UNUSED_ARG(err); /* only used for debug output */
-  LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_tcp_err_cb: TCP error callback: error %d, arg: %p\n", err, arg));
-  LWIP_ASSERT("mqtt_tcp_err_cb: client != NULL", client != NULL);
-  /* Set conn to null before calling close as pcb is already deallocated*/
-  client->conn = 0;
-  mqtt_close(client, MQTT_CONNECT_DISCONNECTED);
-}
-
-/**
- * TCP poll callback function. @see tcp_poll_fn
- * @param arg MQTT client
- * @param tpcb TCP connection handle
- * @return err ERR_OK
- */
-static err_t
-mqtt_tcp_poll_cb(void *arg, struct altcp_pcb *tpcb)
-{
-  mqtt_client_t *client = (mqtt_client_t *)arg;
-  if (client->conn_state == MQTT_CONNECTED) {
-    /* Try send any remaining buffers from output queue */
-    mqtt_output_send(&client->output, tpcb);
-  }
-  return ERR_OK;
-}
-
-/**
- * TCP connect callback function. @see tcp_connected_fn
- * @param arg MQTT client
- * @param err Always ERR_OK, mqtt_tcp_err_cb is called in case of error
- * @return ERR_OK
- */
-static err_t
-mqtt_tcp_connect_cb(void *arg, struct altcp_pcb *tpcb, err_t err)
-{
-  mqtt_client_t *client = (mqtt_client_t *)arg;
-
-  if (err != ERR_OK) {
-    LWIP_DEBUGF(MQTT_DEBUG_WARN, ("mqtt_tcp_connect_cb: TCP connect error %d\n", err));
-    return err;
-  }
-
-  /* Initiate receiver state */
-  client->msg_idx = 0;
-
-  /* Setup TCP callbacks */
-  altcp_recv(tpcb, mqtt_tcp_recv_cb);
-  altcp_sent(tpcb, mqtt_tcp_sent_cb);
-  altcp_poll(tpcb, mqtt_tcp_poll_cb, 2);
-
-  LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_tcp_connect_cb: TCP connection established to server\n"));
-  /* Enter MQTT connect state */
-  client->conn_state = MQTT_CONNECTING;
-
-  /* Start cyclic timer */
-  sys_timeout(MQTT_CYCLIC_TIMER_INTERVAL * 1000, mqtt_cyclic_timer, client);
-  client->cyclic_tick = 0;
-
-  /* Start transmission from output queue, connect message is the first one out*/
-  mqtt_output_send(&client->output, client->conn);
-
-  return ERR_OK;
-}
-
-
-
-/*---------------------------------------------------------------------------------------------------- */
-/* Public API */
-
-
-/**
- * @ingroup mqtt
- * MQTT publish function.
- * @param client MQTT client
- * @param topic Publish topic string
- * @param payload Data to publish (NULL is allowed)
- * @param payload_length Length of payload (0 is allowed)
- * @param qos Quality of service, 0 1 or 2
- * @param retain MQTT retain flag
- * @param cb Callback to call when publish is complete or has timed out
- * @param arg User supplied argument to publish callback
- * @return ERR_OK if successful
- *         ERR_CONN if client is disconnected
- *         ERR_MEM if short on memory
- */
-err_t
-mqtt_publish(mqtt_client_t *client, const char *topic, const void *payload, u16_t payload_length, u8_t qos, u8_t retain,
-             mqtt_request_cb_t cb, void *arg)
-{
-  struct mqtt_request_t *r;
-  u16_t pkt_id;
-  size_t topic_strlen;
-  size_t total_len;
-  u16_t topic_len;
-  u16_t remaining_length;
-
-  LWIP_ASSERT_CORE_LOCKED();
-  LWIP_ASSERT("mqtt_publish: client != NULL", client);
-  LWIP_ASSERT("mqtt_publish: topic != NULL", topic);
-  LWIP_ERROR("mqtt_publish: TCP disconnected", (client->conn_state != TCP_DISCONNECTED), return ERR_CONN);
-
-  topic_strlen = strlen(topic);
-  LWIP_ERROR("mqtt_publish: topic length overflow", (topic_strlen <= (0xFFFF - 2)), return ERR_ARG);
-  topic_len = (u16_t)topic_strlen;
-  total_len = 2 + topic_len + payload_length;
-
-  if (qos > 0) {
-    total_len += 2;
-    /* Generate pkt_id id for QoS1 and 2 */
-    pkt_id = msg_generate_packet_id(client);
-  } else {
-    /* Use reserved value pkt_id 0 for QoS 0 in request handle */
-    pkt_id = 0;
-  }
-  LWIP_ERROR("mqtt_publish: total length overflow", (total_len <= 0xFFFF), return ERR_ARG);
-  remaining_length = (u16_t)total_len;
-
-  LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_publish: Publish with payload length %d to topic \"%s\"\n", payload_length, topic));
-
-  r = mqtt_create_request(client->req_list, LWIP_ARRAYSIZE(client->req_list), pkt_id, cb, arg);
-  if (r == NULL) {
-    return ERR_MEM;
-  }
-
-  if (mqtt_output_check_space(&client->output, remaining_length) == 0) {
-    mqtt_delete_request(r);
-    return ERR_MEM;
-  }
-  /* Append fixed header */
-  mqtt_output_append_fixed_header(&client->output, MQTT_MSG_TYPE_PUBLISH, 0, qos, retain, remaining_length);
-
-  /* Append Topic */
-  mqtt_output_append_string(&client->output, topic, topic_len);
-
-  /* Append packet if for QoS 1 and 2*/
-  if (qos > 0) {
-    mqtt_output_append_u16(&client->output, pkt_id);
-  }
-
-  /* Append optional publish payload */
-  if ((payload != NULL) && (payload_length > 0)) {
-    mqtt_output_append_buf(&client->output, payload, payload_length);
-  }
-
-  mqtt_append_request(&client->pend_req_queue, r);
-  mqtt_output_send(&client->output, client->conn);
-  return ERR_OK;
-}
-
-
-/**
- * @ingroup mqtt
- * MQTT subscribe/unsubscribe function.
- * @param client MQTT client
- * @param topic topic to subscribe to
- * @param qos Quality of service, 0 1 or 2 (only used for subscribe)
- * @param cb Callback to call when subscribe/unsubscribe reponse is received
- * @param arg User supplied argument to publish callback
- * @param sub 1 for subscribe, 0 for unsubscribe
- * @return ERR_OK if successful, @see err_t enum for other results
- */
-err_t
-mqtt_sub_unsub(mqtt_client_t *client, const char *topic, u8_t qos, mqtt_request_cb_t cb, void *arg, u8_t sub)
-{
-  size_t topic_strlen;
-  size_t total_len;
-  u16_t topic_len;
-  u16_t remaining_length;
-  u16_t pkt_id;
-  struct mqtt_request_t *r;
-
-  LWIP_ASSERT_CORE_LOCKED();
-  LWIP_ASSERT("mqtt_sub_unsub: client != NULL", client);
-  LWIP_ASSERT("mqtt_sub_unsub: topic != NULL", topic);
-
-  topic_strlen = strlen(topic);
-  LWIP_ERROR("mqtt_sub_unsub: topic length overflow", (topic_strlen <= (0xFFFF - 2)), return ERR_ARG);
-  topic_len = (u16_t)topic_strlen;
-  /* Topic string, pkt_id, qos for subscribe */
-  total_len =  topic_len + 2 + 2 + (sub != 0);
-  LWIP_ERROR("mqtt_sub_unsub: total length overflow", (total_len <= 0xFFFF), return ERR_ARG);
-  remaining_length = (u16_t)total_len;
-
-  LWIP_ASSERT("mqtt_sub_unsub: qos < 3", qos < 3);
-  if (client->conn_state == TCP_DISCONNECTED) {
-    LWIP_DEBUGF(MQTT_DEBUG_WARN, ("mqtt_sub_unsub: Can not (un)subscribe in disconnected state\n"));
-    return ERR_CONN;
-  }
-
-  pkt_id = msg_generate_packet_id(client);
-  r = mqtt_create_request(client->req_list, LWIP_ARRAYSIZE(client->req_list), pkt_id, cb, arg);
-  if (r == NULL) {
-    return ERR_MEM;
-  }
-
-  if (mqtt_output_check_space(&client->output, remaining_length) == 0) {
-    mqtt_delete_request(r);
-    return ERR_MEM;
-  }
-
-  LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_sub_unsub: Client (un)subscribe to topic \"%s\", id: %d\n", topic, pkt_id));
-
-  mqtt_output_append_fixed_header(&client->output, sub ? MQTT_MSG_TYPE_SUBSCRIBE : MQTT_MSG_TYPE_UNSUBSCRIBE, 0, 1, 0, remaining_length);
-  /* Packet id */
-  mqtt_output_append_u16(&client->output, pkt_id);
-  /* Topic */
-  mqtt_output_append_string(&client->output, topic, topic_len);
-  /* QoS */
-  if (sub != 0) {
-    mqtt_output_append_u8(&client->output, LWIP_MIN(qos, 2));
-  }
-
-  mqtt_append_request(&client->pend_req_queue, r);
-  mqtt_output_send(&client->output, client->conn);
-  return ERR_OK;
-}
-
-
-/**
- * @ingroup mqtt
- * Set callback to handle incoming publish requests from server
- * @param client MQTT client
- * @param pub_cb Callback invoked when publish starts, contain topic and total length of payload
- * @param data_cb Callback for each fragment of payload that arrives
- * @param arg User supplied argument to both callbacks
- */
-void
-mqtt_set_inpub_callback(mqtt_client_t *client, mqtt_incoming_publish_cb_t pub_cb,
-                        mqtt_incoming_data_cb_t data_cb, void *arg)
-{
-  LWIP_ASSERT_CORE_LOCKED();
-  LWIP_ASSERT("mqtt_set_inpub_callback: client != NULL", client != NULL);
-  client->data_cb = data_cb;
-  client->pub_cb = pub_cb;
-  client->inpub_arg = arg;
-}
-
-/**
- * @ingroup mqtt
- * Create a new MQTT client instance
- * @return Pointer to instance on success, NULL otherwise
- */
-mqtt_client_t *
-mqtt_client_new(void)
-{
-  LWIP_ASSERT_CORE_LOCKED();
-  return (mqtt_client_t *)mem_calloc(1, sizeof(mqtt_client_t));
-}
-
-/**
- * @ingroup mqtt
- * Free MQTT client instance
- * @param client Pointer to instance to be freed
- */
-void
-mqtt_client_free(mqtt_client_t *client)
-{
-  mem_free(client);
-}
-
-/**
- * @ingroup mqtt
- * Connect to MQTT server
- * @param client MQTT client
- * @param ip_addr Server IP
- * @param port Server port
- * @param cb Connection state change callback
- * @param arg User supplied argument to connection callback
- * @param client_info Client identification and connection options
- * @return ERR_OK if successful, @see err_t enum for other results
- */
-err_t
-mqtt_client_connect(mqtt_client_t *client, const ip_addr_t *ip_addr, u16_t port, mqtt_connection_cb_t cb, void *arg,
-                    const struct mqtt_connect_client_info_t *client_info)
-{
-  err_t err;
-  size_t len;
-  u16_t client_id_length;
-  /* Length is the sum of 2+"MQTT", protocol level, flags and keep alive */
-  u16_t remaining_length = 2 + 4 + 1 + 1 + 2;
-  u8_t flags = 0, will_topic_len = 0, will_msg_len = 0;
-  u16_t client_user_len = 0, client_pass_len = 0;
-
-  LWIP_ASSERT_CORE_LOCKED();
-  LWIP_ASSERT("mqtt_client_connect: client != NULL", client != NULL);
-  LWIP_ASSERT("mqtt_client_connect: ip_addr != NULL", ip_addr != NULL);
-  LWIP_ASSERT("mqtt_client_connect: client_info != NULL", client_info != NULL);
-  LWIP_ASSERT("mqtt_client_connect: client_info->client_id != NULL", client_info->client_id != NULL);
-
-  if (client->conn_state != TCP_DISCONNECTED) {
-    LWIP_DEBUGF(MQTT_DEBUG_WARN, ("mqtt_client_connect: Already connected\n"));
-    return ERR_ISCONN;
-  }
-
-  /* Wipe clean */
-  memset(client, 0, sizeof(mqtt_client_t));
-  client->connect_arg = arg;
-  client->connect_cb = cb;
-  client->keep_alive = client_info->keep_alive;
-  mqtt_init_requests(client->req_list, LWIP_ARRAYSIZE(client->req_list));
-
-  /* Build connect message */
-  if (client_info->will_topic != NULL && client_info->will_msg != NULL) {
-    flags |= MQTT_CONNECT_FLAG_WILL;
-    flags |= (client_info->will_qos & 3) << 3;
-    if (client_info->will_retain) {
-      flags |= MQTT_CONNECT_FLAG_WILL_RETAIN;
-    }
-    len = strlen(client_info->will_topic);
-    LWIP_ERROR("mqtt_client_connect: client_info->will_topic length overflow", len <= 0xFF, return ERR_VAL);
-    LWIP_ERROR("mqtt_client_connect: client_info->will_topic length must be > 0", len > 0, return ERR_VAL);
-    will_topic_len = (u8_t)len;
-    len = strlen(client_info->will_msg);
-    LWIP_ERROR("mqtt_client_connect: client_info->will_msg length overflow", len <= 0xFF, return ERR_VAL);
-    will_msg_len = (u8_t)len;
-    len = remaining_length + 2 + will_topic_len + 2 + will_msg_len;
-    LWIP_ERROR("mqtt_client_connect: remaining_length overflow", len <= 0xFFFF, return ERR_VAL);
-    remaining_length = (u16_t)len;
-  }
-  if (client_info->client_user != NULL) {
-    flags |= MQTT_CONNECT_FLAG_USERNAME;
-    len = strlen(client_info->client_user);
-    LWIP_ERROR("mqtt_client_connect: client_info->client_user length overflow", len <= 0xFFFF, return ERR_VAL);
-    LWIP_ERROR("mqtt_client_connect: client_info->client_user length must be > 0", len > 0, return ERR_VAL);
-    client_user_len = (u16_t)len;
-    len = remaining_length + 2 + client_user_len;
-    LWIP_ERROR("mqtt_client_connect: remaining_length overflow", len <= 0xFFFF, return ERR_VAL);
-    remaining_length = (u16_t)len;
-  }
-  if (client_info->client_pass != NULL) {
-    flags |= MQTT_CONNECT_FLAG_PASSWORD;
-    len = strlen(client_info->client_pass);
-    LWIP_ERROR("mqtt_client_connect: client_info->client_pass length overflow", len <= 0xFFFF, return ERR_VAL);
-    LWIP_ERROR("mqtt_client_connect: client_info->client_pass length must be > 0", len > 0, return ERR_VAL);
-    client_pass_len = (u16_t)len;
-    len = remaining_length + 2 + client_pass_len;
-    LWIP_ERROR("mqtt_client_connect: remaining_length overflow", len <= 0xFFFF, return ERR_VAL);
-    remaining_length = (u16_t)len;
-  }
-
-  /* Don't complicate things, always connect using clean session */
-  flags |= MQTT_CONNECT_FLAG_CLEAN_SESSION;
-
-  len = strlen(client_info->client_id);
-  LWIP_ERROR("mqtt_client_connect: client_info->client_id length overflow", len <= 0xFFFF, return ERR_VAL);
-  client_id_length = (u16_t)len;
-  len = remaining_length + 2 + client_id_length;
-  LWIP_ERROR("mqtt_client_connect: remaining_length overflow", len <= 0xFFFF, return ERR_VAL);
-  remaining_length = (u16_t)len;
-
-  if (mqtt_output_check_space(&client->output, remaining_length) == 0) {
-    return ERR_MEM;
-  }
-
-#if LWIP_ALTCP && LWIP_ALTCP_TLS
-  if (client_info->tls_config) {
-    client->conn = altcp_tls_new(client_info->tls_config, IP_GET_TYPE(ip_addr));
-  } else
-#endif
-  {
-    client->conn = altcp_tcp_new_ip_type(IP_GET_TYPE(ip_addr));
-  }
-  if (client->conn == NULL) {
-    return ERR_MEM;
-  }
-
-  /* Set arg pointer for callbacks */
-  altcp_arg(client->conn, client);
-  /* Any local address, pick random local port number */
-  err = altcp_bind(client->conn, IP_ADDR_ANY, 0);
-  if (err != ERR_OK) {
-    LWIP_DEBUGF(MQTT_DEBUG_WARN, ("mqtt_client_connect: Error binding to local ip/port, %d\n", err));
-    goto tcp_fail;
-  }
-  LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_client_connect: Connecting to host: %s at port:%"U16_F"\n", ipaddr_ntoa(ip_addr), port));
-
-  /* Connect to server */
-  err = altcp_connect(client->conn, ip_addr, port, mqtt_tcp_connect_cb);
-  if (err != ERR_OK) {
-    LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_client_connect: Error connecting to remote ip/port, %d\n", err));
-    goto tcp_fail;
-  }
-  /* Set error callback */
-  altcp_err(client->conn, mqtt_tcp_err_cb);
-  client->conn_state = TCP_CONNECTING;
-
-  /* Append fixed header */
-  mqtt_output_append_fixed_header(&client->output, MQTT_MSG_TYPE_CONNECT, 0, 0, 0, remaining_length);
-  /* Append Protocol string */
-  mqtt_output_append_string(&client->output, "MQTT", 4);
-  /* Append Protocol level */
-  mqtt_output_append_u8(&client->output, 4);
-  /* Append connect flags */
-  mqtt_output_append_u8(&client->output, flags);
-  /* Append keep-alive */
-  mqtt_output_append_u16(&client->output, client_info->keep_alive);
-  /* Append client id */
-  mqtt_output_append_string(&client->output, client_info->client_id, client_id_length);
-  /* Append will message if used */
-  if ((flags & MQTT_CONNECT_FLAG_WILL) != 0) {
-    mqtt_output_append_string(&client->output, client_info->will_topic, will_topic_len);
-    mqtt_output_append_string(&client->output, client_info->will_msg, will_msg_len);
-  }
-  /* Append user name if given */
-  if ((flags & MQTT_CONNECT_FLAG_USERNAME) != 0) {
-    mqtt_output_append_string(&client->output, client_info->client_user, client_user_len);
-  }
-  /* Append password if given */
-  if ((flags & MQTT_CONNECT_FLAG_PASSWORD) != 0) {
-    mqtt_output_append_string(&client->output, client_info->client_pass, client_pass_len);
-  }
-  return ERR_OK;
-
-tcp_fail:
-  altcp_abort(client->conn);
-  client->conn = NULL;
-  return err;
-}
-
-
-/**
- * @ingroup mqtt
- * Disconnect from MQTT server
- * @param client MQTT client
- */
-void
-mqtt_disconnect(mqtt_client_t *client)
-{
-  LWIP_ASSERT_CORE_LOCKED();
-  LWIP_ASSERT("mqtt_disconnect: client != NULL", client);
-  /* If connection in not already closed */
-  if (client->conn_state != TCP_DISCONNECTED) {
-    /* Set conn_state before calling mqtt_close to prevent callback from being called */
-    client->conn_state = TCP_DISCONNECTED;
-    mqtt_close(client, (mqtt_connection_status_t)0);
-  }
-}
-
-/**
- * @ingroup mqtt
- * Check connection with server
- * @param client MQTT client
- * @return 1 if connected to server, 0 otherwise
- */
-u8_t
-mqtt_client_is_connected(mqtt_client_t *client)
-{
-  LWIP_ASSERT_CORE_LOCKED();
-  LWIP_ASSERT("mqtt_client_is_connected: client != NULL", client);
-  return client->conn_state == MQTT_CONNECTED;
-}
-
-#endif /* LWIP_TCP && LWIP_CALLBACK_API */
+/**
+ * @file
+ * MQTT client
+ *
+ * @defgroup mqtt MQTT client
+ * @ingroup apps
+ * @verbinclude mqtt_client.txt
+ */
+
+/*
+ * Copyright (c) 2016 Erik Andersson <erian747@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack
+ *
+ * Author: Erik Andersson <erian747@gmail.com>
+ *
+ *
+ * @todo:
+ * - Handle large outgoing payloads for PUBLISH messages
+ * - Fix restriction of a single topic in each (UN)SUBSCRIBE message (protocol has support for multiple topics)
+ * - Add support for legacy MQTT protocol version
+ *
+ * Please coordinate changes and requests with Erik Andersson
+ * Erik Andersson <erian747@gmail.com>
+ *
+ */
+#include "lwip/apps/mqtt.h"
+#include "lwip/timeouts.h"
+#include "lwip/ip_addr.h"
+#include "lwip/mem.h"
+#include "lwip/err.h"
+#include "lwip/pbuf.h"
+#include "lwip/tcp.h"
+#include <string.h>
+
+#if LWIP_TCP && LWIP_CALLBACK_API
+
+/**
+ * MQTT_DEBUG: Default is off.
+ */
+#if !defined MQTT_DEBUG || defined __DOXYGEN__
+#define MQTT_DEBUG                  LWIP_DBG_OFF
+#endif
+
+#define MQTT_DEBUG_TRACE        (MQTT_DEBUG | LWIP_DBG_TRACE)
+#define MQTT_DEBUG_STATE        (MQTT_DEBUG | LWIP_DBG_STATE)
+#define MQTT_DEBUG_WARN         (MQTT_DEBUG | LWIP_DBG_LEVEL_WARNING)
+#define MQTT_DEBUG_WARN_STATE   (MQTT_DEBUG | LWIP_DBG_LEVEL_WARNING | LWIP_DBG_STATE)
+#define MQTT_DEBUG_SERIOUS      (MQTT_DEBUG | LWIP_DBG_LEVEL_SERIOUS)
+
+static void mqtt_cyclic_timer(void *arg);
+
+/**
+ * MQTT client connection states
+ */
+enum {
+  TCP_DISCONNECTED,
+  TCP_CONNECTING,
+  MQTT_CONNECTING,
+  MQTT_CONNECTED
+};
+
+/**
+ * MQTT control message types
+ */
+enum mqtt_message_type {
+  MQTT_MSG_TYPE_CONNECT = 1,
+  MQTT_MSG_TYPE_CONNACK = 2,
+  MQTT_MSG_TYPE_PUBLISH = 3,
+  MQTT_MSG_TYPE_PUBACK = 4,
+  MQTT_MSG_TYPE_PUBREC = 5,
+  MQTT_MSG_TYPE_PUBREL = 6,
+  MQTT_MSG_TYPE_PUBCOMP = 7,
+  MQTT_MSG_TYPE_SUBSCRIBE = 8,
+  MQTT_MSG_TYPE_SUBACK = 9,
+  MQTT_MSG_TYPE_UNSUBSCRIBE = 10,
+  MQTT_MSG_TYPE_UNSUBACK = 11,
+  MQTT_MSG_TYPE_PINGREQ = 12,
+  MQTT_MSG_TYPE_PINGRESP = 13,
+  MQTT_MSG_TYPE_DISCONNECT = 14
+};
+
+/** Helpers to extract control packet type and qos from first byte in fixed header */
+#define MQTT_CTL_PACKET_TYPE(fixed_hdr_byte0) ((fixed_hdr_byte0 & 0xf0) >> 4)
+#define MQTT_CTL_PACKET_QOS(fixed_hdr_byte0) ((fixed_hdr_byte0 & 0x6) >> 1)
+
+/**
+ * MQTT connect flags, only used in CONNECT message
+ */
+enum mqtt_connect_flag {
+  MQTT_CONNECT_FLAG_USERNAME = 1 << 7,
+  MQTT_CONNECT_FLAG_PASSWORD = 1 << 6,
+  MQTT_CONNECT_FLAG_WILL_RETAIN = 1 << 5,
+  MQTT_CONNECT_FLAG_WILL = 1 << 2,
+  MQTT_CONNECT_FLAG_CLEAN_SESSION = 1 << 1
+};
+
+
+#if defined(LWIP_DEBUG)
+static const char * const mqtt_message_type_str[15] =
+{
+  "UNDEFINED",
+  "CONNECT",
+  "CONNACK",
+  "PUBLISH",
+  "PUBACK",
+  "PUBREC",
+  "PUBREL",
+  "PUBCOMP",
+  "SUBSCRIBE",
+  "SUBACK",
+  "UNSUBSCRIBE",
+  "UNSUBACK",
+  "PINGREQ",
+  "PINGRESP",
+  "DISCONNECT"
+};
+
+/**
+ * Message type value to string
+ * @param msg_type see enum mqtt_message_type
+ * 
+ * @return Control message type text string
+ */
+static const char *
+mqtt_msg_type_to_str(u8_t msg_type)
+{
+  if (msg_type >= LWIP_ARRAYSIZE(mqtt_message_type_str)) {
+    msg_type = 0;
+  }
+  return mqtt_message_type_str[msg_type];
+}
+
+#endif
+
+
+/**
+ * Generate MQTT packet identifier
+ * @param client MQTT client
+ * @return New packet identifier, range 1 to 65535
+ */
+static u16_t
+msg_generate_packet_id(mqtt_client_t *client)
+{
+  client->pkt_id_seq++;
+  if (client->pkt_id_seq == 0) {
+    client->pkt_id_seq++;
+  }
+  return client->pkt_id_seq;
+}
+
+/*--------------------------------------------------------------------------------------------------------------------- */
+/* Output ring buffer */
+
+
+#define MQTT_RINGBUF_IDX_MASK ((MQTT_OUTPUT_RINGBUF_SIZE) - 1)
+
+/** Add single item to ring buffer */
+#define mqtt_ringbuf_put(rb, item) ((rb)->buf)[(rb)->put++ & MQTT_RINGBUF_IDX_MASK] = (item)
+
+/** Return number of bytes in ring buffer */
+#define mqtt_ringbuf_len(rb) ((u16_t)((rb)->put - (rb)->get))
+
+/** Return number of bytes free in ring buffer */
+#define mqtt_ringbuf_free(rb) (MQTT_OUTPUT_RINGBUF_SIZE - mqtt_ringbuf_len(rb))
+
+/** Return number of bytes possible to read without wrapping around */
+#define mqtt_ringbuf_linear_read_length(rb) LWIP_MIN(mqtt_ringbuf_len(rb), (MQTT_OUTPUT_RINGBUF_SIZE - ((rb)->get & MQTT_RINGBUF_IDX_MASK)))
+
+/** Return pointer to ring buffer get position */
+#define mqtt_ringbuf_get_ptr(rb) (&(rb)->buf[(rb)->get & MQTT_RINGBUF_IDX_MASK])
+
+#define mqtt_ringbuf_advance_get_idx(rb, len) ((rb)->get += (len))
+
+
+/**
+ * Try send as many bytes as possible from output ring buffer
+ * @param rb Output ring buffer
+ * @param tpcb TCP connection handle
+ */
+static void
+mqtt_output_send(struct mqtt_ringbuf_t *rb, struct tcp_pcb *tpcb)
+{
+  err_t err;
+  u8_t wrap = 0;
+  u16_t ringbuf_lin_len = mqtt_ringbuf_linear_read_length(rb);
+  u16_t send_len = tcp_sndbuf(tpcb);
+  LWIP_ASSERT("mqtt_output_send: tpcb != NULL", tpcb != NULL);
+
+  //printf("MQTT: r_l_l: %u, s_l: %u\r\n", ringbuf_lin_len, send_len);
+  vTaskDelay(10);
+
+  if (send_len == 0 || ringbuf_lin_len == 0) {
+    return;
+  }
+
+  LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_output_send: tcp_sndbuf: %d bytes, ringbuf_linear_available: %d, get %d, put %d\n",
+                                send_len, ringbuf_lin_len, ((rb)->get & MQTT_RINGBUF_IDX_MASK), ((rb)->put & MQTT_RINGBUF_IDX_MASK)));
+
+  if (send_len > ringbuf_lin_len) {
+    /* Space in TCP output buffer is larger than available in ring buffer linear portion */
+    send_len = ringbuf_lin_len;
+    /* Wrap around if more data in ring buffer after linear portion */
+    wrap = (mqtt_ringbuf_len(rb) > ringbuf_lin_len);
+  }
+  err = tcp_write(tpcb, mqtt_ringbuf_get_ptr(rb), send_len, TCP_WRITE_FLAG_COPY | (wrap ? TCP_WRITE_FLAG_MORE : 0));
+  if ((err == ERR_OK) && wrap) {
+    mqtt_ringbuf_advance_get_idx(rb, send_len);
+    /* Use the lesser one of ring buffer linear length and TCP send buffer size */
+    send_len = LWIP_MIN(tcp_sndbuf(tpcb), mqtt_ringbuf_linear_read_length(rb));
+    err = tcp_write(tpcb, mqtt_ringbuf_get_ptr(rb), send_len, TCP_WRITE_FLAG_COPY);
+  }
+
+  if (err == ERR_OK) {
+    mqtt_ringbuf_advance_get_idx(rb, send_len);
+    /* Flush */
+    tcp_output(tpcb);
+  } else {
+    LWIP_DEBUGF(MQTT_DEBUG_WARN, ("mqtt_output_send: Send failed with err %d (\"%s\")\n", err, lwip_strerr(err)));
+  }
+}
+
+
+
+/*--------------------------------------------------------------------------------------------------------------------- */
+/* Request queue */
+
+/**
+ * Create request item
+ * @param r_objs Pointer to request objects
+ * @param pkt_id Packet identifier of request
+ * @param cb Packet callback to call when requests lifetime ends
+ * @param arg Parameter following callback
+ * @return Request or NULL if failed to create
+ */
+static struct mqtt_request_t *
+mqtt_create_request(struct mqtt_request_t *r_objs, u16_t pkt_id, mqtt_request_cb_t cb, void *arg)
+{
+  struct mqtt_request_t *r = NULL;
+  u8_t n;
+  LWIP_ASSERT("mqtt_create_request: r_objs != NULL", r_objs != NULL);
+  for (n = 0; n < MQTT_REQ_MAX_IN_FLIGHT; n++) {
+    /* Item point to itself if not in use */
+    if (r_objs[n].next == &r_objs[n]) {
+      r = &r_objs[n];
+      r->next = NULL;
+      r->cb = cb;
+      r->arg = arg;
+      r->pkt_id = pkt_id;
+      break;
+    }
+  }
+  return r;
+}
+
+
+/**
+ * Append request to pending request queue
+ * @param tail Pointer to request queue tail pointer
+ * @param r Request to append
+ */
+static void
+mqtt_append_request(struct mqtt_request_t **tail, struct mqtt_request_t *r)
+{
+  struct mqtt_request_t *head = NULL;
+  s16_t time_before = 0;
+  struct mqtt_request_t *iter;
+
+  LWIP_ASSERT("mqtt_append_request: tail != NULL", tail != NULL);
+
+  /* Iterate trough queue to find head, and count total timeout time */
+  for (iter = *tail; iter != NULL; iter = iter->next) {
+    time_before += iter->timeout_diff;
+    head = iter;
+  }
+
+  LWIP_ASSERT("mqtt_append_request: time_before <= MQTT_REQ_TIMEOUT", time_before <= MQTT_REQ_TIMEOUT);
+  r->timeout_diff = MQTT_REQ_TIMEOUT - time_before;
+  if (head == NULL) {
+    *tail = r;
+  } else {
+    head->next = r;
+  }
+}
+
+
+/**
+ * Delete request item
+ * @param r Request item to delete
+ */
+static void
+mqtt_delete_request(struct mqtt_request_t *r)
+{
+  if (r != NULL) {
+    r->next = r;
+  }
+}
+
+/**
+ * Remove a request item with a specific packet identifier from request queue
+ * @param tail Pointer to request queue tail pointer
+ * @param pkt_id Packet identifier of request to take
+ * @return Request item if found, NULL if not
+ */
+static struct mqtt_request_t *
+mqtt_take_request(struct mqtt_request_t **tail, u16_t pkt_id)
+{
+  struct mqtt_request_t *iter = NULL, *prev = NULL;
+  LWIP_ASSERT("mqtt_take_request: tail != NULL", tail != NULL);
+  /* Search all request for pkt_id */
+  for (iter = *tail; iter != NULL; iter = iter->next) {
+    if (iter->pkt_id == pkt_id) {
+      break;
+    }
+    prev = iter;
+  }
+
+  /* If request was found */
+  if (iter != NULL) {
+    /* unchain */
+    if (prev == NULL) {
+      *tail= iter->next;
+    } else {
+      prev->next = iter->next;
+    }
+    /* If exists, add remaining timeout time for the request to next */
+    if (iter->next != NULL) {
+      iter->next->timeout_diff += iter->timeout_diff;
+    }
+    iter->next = NULL;
+  }
+  return iter;
+}
+
+/**
+ * Handle requests timeout
+ * @param tail Pointer to request queue tail pointer
+ * @param t Time since last call in seconds
+ */
+static void
+mqtt_request_time_elapsed(struct mqtt_request_t **tail, u8_t t)
+{
+  struct mqtt_request_t *r = *tail;
+  LWIP_ASSERT("mqtt_request_time_elapsed: tail != NULL", tail != NULL);
+  while (t > 0 && r != NULL) {
+    if (t >= r->timeout_diff) {
+      t -= (u8_t)r->timeout_diff;
+      /* Unchain */
+      *tail = r->next;
+      /* Notify upper layer about timeout */
+      if (r->cb != NULL) {
+        r->cb(r->arg, ERR_TIMEOUT);
+      }
+      mqtt_delete_request(r);
+      /* Tail might be be modified in callback, so re-read it in every iteration */
+      r = *(struct mqtt_request_t * const volatile *)tail;
+    } else {
+      r->timeout_diff -= t;
+      t = 0;
+    }
+  }
+}
+
+/**
+ * Free all request items
+ * @param tail Pointer to request queue tail pointer
+ */
+static void
+mqtt_clear_requests(struct mqtt_request_t **tail)
+{
+  struct mqtt_request_t *iter, *next;
+  LWIP_ASSERT("mqtt_clear_requests: tail != NULL", tail != NULL);
+  for (iter = *tail; iter != NULL; iter = next) {
+    next = iter->next;
+    mqtt_delete_request(iter);
+  }
+  *tail = NULL;
+}
+/**
+ * Initialize all request items
+ * @param r_objs Pointer to request objects
+ */
+static void
+mqtt_init_requests(struct mqtt_request_t *r_objs)
+{
+  u8_t n;
+  LWIP_ASSERT("mqtt_init_requests: r_objs != NULL", r_objs != NULL);
+  for (n = 0; n < MQTT_REQ_MAX_IN_FLIGHT; n++) {
+    /* Item pointing to itself indicates unused */
+    r_objs[n].next = &r_objs[n];
+  }
+}
+
+/*--------------------------------------------------------------------------------------------------------------------- */
+/* Output message build helpers */
+
+
+static void
+mqtt_output_append_u8(struct mqtt_ringbuf_t *rb, u8_t value)
+{
+  mqtt_ringbuf_put(rb, value);
+}
+
+static
+void mqtt_output_append_u16(struct mqtt_ringbuf_t *rb, u16_t value)
+{
+  mqtt_ringbuf_put(rb, value >> 8);
+  mqtt_ringbuf_put(rb, value & 0xff);
+}
+
+static void
+mqtt_output_append_buf(struct mqtt_ringbuf_t *rb, const void *data, u16_t length)
+{
+  u16_t n;
+  for (n = 0; n < length; n++) {
+    mqtt_ringbuf_put(rb, ((const u8_t *)data)[n]);
+  }
+}
+
+static void
+mqtt_output_append_string(struct mqtt_ringbuf_t *rb, const char *str, u16_t length)
+{
+  u16_t n;
+  mqtt_ringbuf_put(rb, length >> 8);
+  mqtt_ringbuf_put(rb, length & 0xff);
+  for (n = 0; n < length; n++) {
+    mqtt_ringbuf_put(rb, str[n]);
+  }
+}
+
+/**
+ * Append fixed header
+ * @param rb Output ring buffer
+ * @param msg_type see enum mqtt_message_type
+ * @param dup MQTT DUP flag
+ * @param qos MQTT QoS field
+ * @param retain MQTT retain flag
+ * @param r_length Remaining length after fixed header
+ */
+
+static void
+mqtt_output_append_fixed_header(struct mqtt_ringbuf_t *rb, u8_t msg_type, u8_t dup,
+                 u8_t qos, u8_t retain, u16_t r_length)
+{
+  /* Start with control byte */
+  mqtt_output_append_u8(rb, (((msg_type & 0x0f) << 4) | ((dup & 1) << 3) | ((qos & 3) << 1) | (retain & 1)));
+  /* Encode remaining length field */
+  do {
+    mqtt_output_append_u8(rb, (r_length & 0x7f) | (r_length >= 128 ? 0x80 : 0));
+    r_length >>= 7;
+  } while (r_length > 0);
+}
+
+
+/**
+ * Check output buffer space
+ * @param rb Output ring buffer
+ * @param r_length Remaining length after fixed header
+ * @return 1 if message will fit, 0 if not enough buffer space
+ */
+static u8_t
+mqtt_output_check_space(struct mqtt_ringbuf_t *rb, u16_t r_length)
+{
+  /* Start with length of type byte + remaining length */
+  u16_t total_len = 1 + r_length;
+
+  LWIP_ASSERT("mqtt_output_check_space: rb != NULL", rb != NULL);
+
+ /* Calculate number of required bytes to contain the remaining bytes field and add to total*/
+  do {
+    total_len++;
+    r_length >>= 7;
+  } while (r_length > 0);
+
+  return (total_len <= mqtt_ringbuf_free(rb));
+}
+
+
+/**
+ * Close connection to server
+ * @param client MQTT client
+ * @param reason Reason for disconnection
+ */
+static void
+mqtt_close(mqtt_client_t *client, mqtt_connection_status_t reason)
+{
+  LWIP_ASSERT("mqtt_close: client != NULL", client != NULL);
+
+  /* Bring down TCP connection if not already done */
+  if (client->conn != NULL) {
+    err_t res;
+    tcp_recv(client->conn, NULL);
+    tcp_err(client->conn,  NULL);
+    tcp_sent(client->conn, NULL);
+    res = tcp_close(client->conn);
+    if (res != ERR_OK) {
+      tcp_abort(client->conn);
+      LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_close: Close err=%s\n", lwip_strerr(res)));
+    }
+    client->conn = NULL;
+  }
+
+  /* Remove all pending requests */
+  mqtt_clear_requests(&client->pend_req_queue);
+  /* Stop cyclic timer */
+  sys_untimeout(mqtt_cyclic_timer, client);
+
+  /* Notify upper layer of disconnection if changed state */
+  if (client->conn_state != TCP_DISCONNECTED) {
+
+    client->conn_state = TCP_DISCONNECTED;
+    if (client->connect_cb != NULL) {
+      client->connect_cb(client, client->connect_arg, reason);
+    }
+  }
+}
+
+
+/**
+ * Interval timer, called every MQTT_CYCLIC_TIMER_INTERVAL seconds in MQTT_CONNECTING and MQTT_CONNECTED states
+ * @param arg MQTT client
+ */
+static void
+mqtt_cyclic_timer(void *arg)
+{
+  u8_t restart_timer = 1;
+  mqtt_client_t *client = (mqtt_client_t *)arg;
+  LWIP_ASSERT("mqtt_cyclic_timer: client != NULL", client != NULL);
+
+  if (client->conn_state == MQTT_CONNECTING) {
+    client->cyclic_tick++;
+    if ((client->cyclic_tick * MQTT_CYCLIC_TIMER_INTERVAL) >= MQTT_CONNECT_TIMOUT) {
+      LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_cyclic_timer: CONNECT attempt to server timed out\n"));
+      /* Disconnect TCP */
+      mqtt_close(client, MQTT_CONNECT_TIMEOUT);
+      restart_timer = 0;
+    }
+  } else if (client->conn_state == MQTT_CONNECTED) {
+    /* Handle timeout for pending requests */
+    mqtt_request_time_elapsed(&client->pend_req_queue, MQTT_CYCLIC_TIMER_INTERVAL);
+
+    /* keep_alive > 0 means keep alive functionality shall be used */
+    if (client->keep_alive > 0) {
+
+      client->server_watchdog++;
+      /* If reception from server has been idle for 1.5*keep_alive time, server is considered unresponsive */
+      if ((client->server_watchdog * MQTT_CYCLIC_TIMER_INTERVAL) > (client->keep_alive + client->keep_alive/2)) {
+        LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_cyclic_timer: Server incoming keep-alive timeout\n"));
+        mqtt_close(client, MQTT_CONNECT_TIMEOUT);
+        restart_timer = 0;
+      }
+
+      /* If time for a keep alive message to be sent, transmission has been idle for keep_alive time */
+      if ((client->cyclic_tick * MQTT_CYCLIC_TIMER_INTERVAL) >= client->keep_alive) {
+        LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_cyclic_timer: Sending keep-alive message to server\n"));
+        if (mqtt_output_check_space(&client->output, 0) != 0) {
+          mqtt_output_append_fixed_header(&client->output, MQTT_MSG_TYPE_PINGREQ, 0, 0, 0, 0);
+          client->cyclic_tick = 0;
+        }
+      } else {
+        client->cyclic_tick++;
+      }
+    }
+  } else {
+    LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_cyclic_timer: Timer should not be running in state %d\n", client->conn_state));
+    restart_timer = 0;
+  }
+  if (restart_timer) {
+    sys_timeout(MQTT_CYCLIC_TIMER_INTERVAL*1000, mqtt_cyclic_timer, arg);
+  }
+}
+
+
+/**
+ * Send PUBACK, PUBREC or PUBREL response message
+ * @param client MQTT client
+ * @param msg PUBACK, PUBREC or PUBREL
+ * @param pkt_id Packet identifier
+ * @param qos QoS value
+ * @return ERR_OK if successful, ERR_MEM if out of memory
+ */
+static err_t
+pub_ack_rec_rel_response(mqtt_client_t *client, u8_t msg, u16_t pkt_id, u8_t qos)
+{
+  err_t err = ERR_OK;
+  if (mqtt_output_check_space(&client->output, 2)) {
+    mqtt_output_append_fixed_header(&client->output, msg, 0, qos, 0, 2);
+    mqtt_output_append_u16(&client->output, pkt_id);
+    mqtt_output_send(&client->output, client->conn);
+  } else {
+    LWIP_DEBUGF(MQTT_DEBUG_TRACE,("pub_ack_rec_rel_response: OOM creating response: %s with pkt_id: %d\n",
+                                  mqtt_msg_type_to_str(msg), pkt_id));
+    err = ERR_MEM;
+  }
+  return err;
+}
+
+/**
+ * Subscribe response from server
+ * @param r Matching request
+ * @param result Result code from server
+ */
+static void
+mqtt_incomming_suback(struct mqtt_request_t *r, u8_t result)
+{
+  if (r->cb != NULL) {
+    r->cb(r->arg, result < 3 ? ERR_OK : ERR_ABRT);
+  }
+}
+
+
+/**
+ * Complete MQTT message received or buffer full
+ * @param client MQTT client
+ * @param fixed_hdr_idx header index
+ * @param length length received part
+ * @param remaining_length Remaining length of complete message
+ */
+static mqtt_connection_status_t
+  mqtt_message_received(mqtt_client_t *client, u8_t fixed_hdr_idx, u16_t length, u32_t remaining_length)
+{
+  mqtt_connection_status_t res = MQTT_CONNECT_ACCEPTED;
+
+  u8_t *var_hdr_payload = client->rx_buffer + fixed_hdr_idx;
+
+  /* Control packet type */
+  u8_t pkt_type = MQTT_CTL_PACKET_TYPE(client->rx_buffer[0]);
+  u16_t pkt_id = 0;
+
+  if (pkt_type == MQTT_MSG_TYPE_CONNACK) {
+    if (client->conn_state == MQTT_CONNECTING) {
+      /* Get result code from CONNACK */
+      res = (mqtt_connection_status_t)var_hdr_payload[1];
+      LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_message_received: Connect response code %d\n", res));
+      if (res == MQTT_CONNECT_ACCEPTED) {
+        /* Reset cyclic_tick when changing to connected state */
+        client->cyclic_tick = 0;
+        client->conn_state = MQTT_CONNECTED;
+        /* Notify upper layer */
+        if (client->connect_cb != 0) {
+          client->connect_cb(client, client->connect_arg, res);
+        }
+      }
+    } else {
+      LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_message_received: Received CONNACK in connected state\n"));
+    }
+  } else if (pkt_type == MQTT_MSG_TYPE_PINGRESP) {
+    LWIP_DEBUGF(MQTT_DEBUG_TRACE,( "mqtt_message_received: Received PINGRESP from server\n"));
+
+  } else if (pkt_type == MQTT_MSG_TYPE_PUBLISH) {
+    u16_t payload_offset = 0;
+    u16_t payload_length = length;
+    u8_t qos = MQTT_CTL_PACKET_QOS(client->rx_buffer[0]);
+
+    if (client->msg_idx <= MQTT_VAR_HEADER_BUFFER_LEN) {
+      /* Should have topic and pkt id*/
+      uint8_t *topic;
+      uint16_t after_topic;
+      u8_t bkp;
+      u16_t topic_len = var_hdr_payload[0];
+      topic_len = (topic_len << 8) + (u16_t)(var_hdr_payload[1]);
+
+      topic = var_hdr_payload + 2;
+      after_topic = 2 + topic_len;
+      /* Check length, add one byte even for QoS 0 so that zero termination will fit */
+      if ((after_topic + (qos? 2 : 1)) > length) {
+        LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_message_received: Receive buffer can not fit topic + pkt_id\n"));
+        goto out_disconnect;
+      }
+
+      /* id for QoS 1 and 2 */
+      if (qos > 0) {
+        client->inpub_pkt_id = ((u16_t)var_hdr_payload[after_topic] << 8) + (u16_t)var_hdr_payload[after_topic + 1];
+        after_topic += 2;
+      } else {
+        client->inpub_pkt_id = 0;
+      }
+      /* Take backup of byte after topic */
+      bkp = topic[topic_len];
+      /* Zero terminate string */
+      topic[topic_len] = 0;
+      /* Payload data remaining in receive buffer */
+      payload_length = length - after_topic;
+      payload_offset = after_topic;
+
+      LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_incomming_publish: Received message with QoS %d at topic: %s, payload length %d\n",
+                                    qos, topic, remaining_length + payload_length));
+      if (client->pub_cb != NULL) {
+        client->pub_cb(client->inpub_arg, (const char *)topic, remaining_length + payload_length);
+      }
+      /* Restore byte after topic */
+      topic[topic_len] = bkp;
+    }
+    if (payload_length > 0 || remaining_length == 0) {
+      client->data_cb(client->inpub_arg, var_hdr_payload + payload_offset, payload_length, remaining_length == 0 ? MQTT_DATA_FLAG_LAST : 0);
+      /* Reply if QoS > 0 */
+      if (remaining_length == 0 && qos > 0) {
+        /* Send PUBACK for QoS 1 or PUBREC for QoS 2 */
+        u8_t resp_msg = (qos == 1) ? MQTT_MSG_TYPE_PUBACK : MQTT_MSG_TYPE_PUBREC;
+        LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_incomming_publish: Sending publish response: %s with pkt_id: %d\n",
+                                      mqtt_msg_type_to_str(resp_msg), client->inpub_pkt_id));
+        pub_ack_rec_rel_response(client, resp_msg, client->inpub_pkt_id, 0);
+      }
+    }
+  } else {
+    /* Get packet identifier */
+    pkt_id = (u16_t)var_hdr_payload[0] << 8;
+    pkt_id |= (u16_t)var_hdr_payload[1];
+    if (pkt_id == 0) {
+      LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_message_received: Got message with illegal packet identifier: 0\n"));
+      goto out_disconnect;
+    }
+    if (pkt_type == MQTT_MSG_TYPE_PUBREC) {
+      LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_message_received: PUBREC, sending PUBREL with pkt_id: %d\n",pkt_id));
+      pub_ack_rec_rel_response(client, MQTT_MSG_TYPE_PUBREL, pkt_id, 1);
+
+    } else if (pkt_type == MQTT_MSG_TYPE_PUBREL) {
+      LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_message_received: PUBREL, sending PUBCOMP response with pkt_id: %d\n",pkt_id));
+      pub_ack_rec_rel_response(client, MQTT_MSG_TYPE_PUBCOMP, pkt_id, 0);
+
+    } else if (pkt_type == MQTT_MSG_TYPE_SUBACK || pkt_type == MQTT_MSG_TYPE_UNSUBACK ||
+              pkt_type == MQTT_MSG_TYPE_PUBCOMP || pkt_type == MQTT_MSG_TYPE_PUBACK) {
+      struct mqtt_request_t *r = mqtt_take_request(&client->pend_req_queue, pkt_id);
+      if (r != NULL) {
+        LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_message_received: %s response with id %d\n", mqtt_msg_type_to_str(pkt_type), pkt_id));
+        if (pkt_type == MQTT_MSG_TYPE_SUBACK) {
+          if (length < 3) {
+            LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_message_received: To small SUBACK packet\n"));
+            goto out_disconnect;
+          } else {
+            mqtt_incomming_suback(r, var_hdr_payload[2]);
+          }
+        } else if (r->cb != NULL) {
+          r->cb(r->arg, ERR_OK);
+        }
+        mqtt_delete_request(r);
+      } else {
+        LWIP_DEBUGF(MQTT_DEBUG_WARN,( "mqtt_message_received: Received %s reply, with wrong pkt_id: %d\n", mqtt_msg_type_to_str(pkt_type), pkt_id));
+      }
+    } else {
+      LWIP_DEBUGF(MQTT_DEBUG_WARN,( "mqtt_message_received: Received unknown message type: %d\n", pkt_type));
+      goto out_disconnect;
+    }
+  }
+  return res;
+out_disconnect:
+  return MQTT_CONNECT_DISCONNECTED;
+}
+
+
+/**
+ * MQTT incoming message parser
+ * @param client MQTT client
+ * @param p PBUF chain of received data
+ * @return Connection status
+ */
+static mqtt_connection_status_t
+mqtt_parse_incoming(mqtt_client_t *client, struct pbuf *p)
+{
+  u16_t in_offset = 0;
+  u32_t msg_rem_len = 0;
+  u8_t fixed_hdr_idx = 0;
+  u8_t b = 0;
+
+  while (p->tot_len > in_offset) {
+    if ((fixed_hdr_idx < 2) || ((b & 0x80) != 0)) {
+
+      if (fixed_hdr_idx < client->msg_idx) {
+        b = client->rx_buffer[fixed_hdr_idx];
+      } else {
+        b = pbuf_get_at(p, in_offset++);
+        client->rx_buffer[client->msg_idx++] = b;
+      }
+      fixed_hdr_idx++;
+
+      if (fixed_hdr_idx >= 2) {
+        msg_rem_len |= (u32_t)(b & 0x7f) << ((fixed_hdr_idx - 2) * 7);
+        if ((b & 0x80) == 0) {
+          LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_parse_incoming: Remaining length after fixed header: %d\n", msg_rem_len));
+          if (msg_rem_len == 0) {
+            /* Complete message with no extra headers of payload received */
+            mqtt_message_received(client, fixed_hdr_idx, 0, 0);
+            client->msg_idx = 0;
+            fixed_hdr_idx = 0;
+          } else {
+            /* Bytes remaining in message */
+            msg_rem_len = (msg_rem_len + fixed_hdr_idx) - client->msg_idx;
+          }
+        }
+      }
+    } else {
+      u16_t cpy_len, cpy_start, buffer_space;
+
+      cpy_start = (client->msg_idx - fixed_hdr_idx) % (MQTT_VAR_HEADER_BUFFER_LEN - fixed_hdr_idx) + fixed_hdr_idx;
+
+      /* Allow to copy the lesser one of available length in input data or bytes remaining in message */
+      cpy_len = (u16_t)LWIP_MIN((u16_t)(p->tot_len - in_offset), msg_rem_len);
+
+      /* Limit to available space in buffer */
+      buffer_space = MQTT_VAR_HEADER_BUFFER_LEN - cpy_start;
+      if (cpy_len > buffer_space) {
+        cpy_len = buffer_space;
+      }
+      pbuf_copy_partial(p, client->rx_buffer+cpy_start, cpy_len, in_offset);
+
+      /* Advance get and put indexes  */
+      client->msg_idx += cpy_len;
+      in_offset += cpy_len;
+      msg_rem_len -= cpy_len;
+
+      LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_parse_incoming: msg_idx: %d, cpy_len: %d, remaining %d\n", client->msg_idx, cpy_len, msg_rem_len));
+      if (msg_rem_len == 0 || cpy_len == buffer_space) {
+        /* Whole message received or buffer is full */
+        mqtt_connection_status_t res = mqtt_message_received(client, fixed_hdr_idx, (cpy_start + cpy_len) - fixed_hdr_idx, msg_rem_len);
+        if (res != MQTT_CONNECT_ACCEPTED) {
+          return res;
+        }
+        if (msg_rem_len == 0) {
+          /* Reset parser state */
+          client->msg_idx = 0;
+          /* msg_tot_len = 0; */
+          fixed_hdr_idx = 0;
+        }
+      }
+    }
+  }
+  return MQTT_CONNECT_ACCEPTED;
+}
+
+
+/**
+ * TCP received callback function. @see tcp_recv_fn
+ * @param arg MQTT client
+ * @param p PBUF chain of received data
+ * @param err Passed as return value if not ERR_OK
+ * @return ERR_OK or err passed into callback
+ */
+static err_t
+mqtt_tcp_recv_cb(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
+{
+  mqtt_client_t *client = (mqtt_client_t *)arg;
+  LWIP_ASSERT("mqtt_tcp_recv_cb: client != NULL", client != NULL);
+  LWIP_ASSERT("mqtt_tcp_recv_cb: client->conn == pcb", client->conn == pcb);
+
+  if (p == NULL) {
+    LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_tcp_recv_cb: Recv pbuf=NULL, remote has closed connection\n"));
+    mqtt_close(client, MQTT_CONNECT_DISCONNECTED);
+  } else {
+    mqtt_connection_status_t res;
+    if (err != ERR_OK) {
+      LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_tcp_recv_cb: Recv err=%d\n", err));
+      pbuf_free(p);
+      return err;
+    }
+
+    /* Tell remote that data has been received */
+    tcp_recved(pcb, p->tot_len);
+    res = mqtt_parse_incoming(client, p);
+    pbuf_free(p);
+
+    if (res != MQTT_CONNECT_ACCEPTED) {
+      mqtt_close(client, res);
+    }
+    /* If keep alive functionality is used */
+    if (client->keep_alive != 0) {
+      /* Reset server alive watchdog */
+      client->server_watchdog = 0;
+    }
+
+  }
+  return ERR_OK;
+}
+
+
+/**
+ * TCP data sent callback function. @see tcp_sent_fn
+ * @param arg MQTT client
+ * @param tpcb TCP connection handle
+ * @param len Number of bytes sent
+ * @return ERR_OK
+ */
+static err_t
+mqtt_tcp_sent_cb(void *arg, struct tcp_pcb *tpcb, u16_t len)
+{
+  mqtt_client_t *client = (mqtt_client_t *)arg;
+
+  LWIP_UNUSED_ARG(tpcb);
+  LWIP_UNUSED_ARG(len);
+
+  if (client->conn_state == MQTT_CONNECTED) {
+    struct mqtt_request_t *r;
+
+    /* Reset keep-alive send timer and server watchdog */
+    client->cyclic_tick = 0;
+    client->server_watchdog = 0;
+    /* QoS 0 publish has no response from server, so call its callbacks here */
+    while ((r = mqtt_take_request(&client->pend_req_queue, 0)) != NULL) {
+      LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_tcp_sent_cb: Calling QoS 0 publish complete callback\n"));
+      if (r->cb != NULL) {
+        r->cb(r->arg, ERR_OK);
+      }
+      mqtt_delete_request(r);
+    }
+    /* Try send any remaining buffers from output queue */
+    mqtt_output_send(&client->output, client->conn);
+  }
+  return ERR_OK;
+}
+
+/**
+ * TCP error callback function. @see tcp_err_fn
+ * @param arg MQTT client
+ * @param err Error encountered
+ */
+static void
+mqtt_tcp_err_cb(void *arg, err_t err)
+{
+  mqtt_client_t *client = (mqtt_client_t *)arg;
+  LWIP_UNUSED_ARG(err); /* only used for debug output */
+  LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_tcp_err_cb: TCP error callback: error %d, arg: %p\n", err, arg));
+  LWIP_ASSERT("mqtt_tcp_err_cb: client != NULL", client != NULL);
+  /* Set conn to null before calling close as pcb is already deallocated*/
+  client->conn = 0;
+  mqtt_close(client, MQTT_CONNECT_DISCONNECTED);
+}
+
+/**
+ * TCP poll callback function. @see tcp_poll_fn
+ * @param arg MQTT client
+ * @param tpcb TCP connection handle
+ * @return err ERR_OK
+ */
+static err_t
+mqtt_tcp_poll_cb(void *arg, struct tcp_pcb *tpcb)
+{
+  mqtt_client_t *client = (mqtt_client_t *)arg;
+  if (client->conn_state == MQTT_CONNECTED) {
+    /* Try send any remaining buffers from output queue */
+    mqtt_output_send(&client->output, tpcb);
+  }
+  return ERR_OK;
+}
+
+/**
+ * TCP connect callback function. @see tcp_connected_fn
+ * @param arg MQTT client
+ * @param err Always ERR_OK, mqtt_tcp_err_cb is called in case of error
+ * @return ERR_OK
+ */
+static err_t
+mqtt_tcp_connect_cb(void *arg, struct tcp_pcb *tpcb, err_t err)
+{
+  mqtt_client_t* client = (mqtt_client_t *)arg;
+
+  if (err != ERR_OK) {
+    LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_tcp_connect_cb: TCP connect error %d\n", err));
+    return err;
+  }
+
+  /* Initiate receiver state */
+  client->msg_idx = 0;
+
+  /* Setup TCP callbacks */
+  tcp_recv(tpcb, mqtt_tcp_recv_cb);
+  tcp_sent(tpcb, mqtt_tcp_sent_cb);
+  tcp_poll(tpcb, mqtt_tcp_poll_cb, 2);
+
+  LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_tcp_connect_cb: TCP connection established to server\n"));
+  /* Enter MQTT connect state */
+  client->conn_state = MQTT_CONNECTING;
+
+  /* Start cyclic timer */
+  sys_timeout(MQTT_CYCLIC_TIMER_INTERVAL*1000, mqtt_cyclic_timer, client);
+  client->cyclic_tick = 0;
+
+  /* Start transmission from output queue, connect message is the first one out*/
+  mqtt_output_send(&client->output, client->conn);
+
+  return ERR_OK;
+}
+
+
+
+/*---------------------------------------------------------------------------------------------------- */
+/* Public API */
+
+
+/**
+ * @ingroup mqtt
+ * MQTT publish function.
+ * @param client MQTT client
+ * @param topic Publish topic string
+ * @param payload Data to publish (NULL is allowed)
+ * @param payload_length: Length of payload (0 is allowed)
+ * @param qos Quality of service, 0 1 or 2
+ * @param retain MQTT retain flag
+ * @param cb Callback to call when publish is complete or has timed out
+ * @param arg User supplied argument to publish callback
+ * @return ERR_OK if successful
+ *         ERR_CONN if client is disconnected
+ *         ERR_MEM if short on memory
+ */
+err_t
+mqtt_publish(mqtt_client_t *client, const char *topic, const void *payload, u16_t payload_length, u8_t qos, u8_t retain,
+             mqtt_request_cb_t cb, void *arg)
+{
+  struct mqtt_request_t *r;
+  u16_t pkt_id;
+  size_t topic_strlen;
+  size_t total_len;
+  u16_t topic_len;
+  u16_t remaining_length;
+
+  LWIP_ASSERT("mqtt_publish: client != NULL", client);
+  LWIP_ASSERT("mqtt_publish: topic != NULL", topic);
+  LWIP_ERROR("mqtt_publish: TCP disconnected", (client->conn_state != TCP_DISCONNECTED), return ERR_CONN);
+
+  topic_strlen = strlen(topic);
+  LWIP_ERROR("mqtt_publish: topic length overflow", (topic_strlen <= (0xFFFF - 2)), return ERR_ARG);
+  topic_len = (u16_t)topic_strlen;
+  total_len = 2 + topic_len + payload_length;
+  LWIP_ERROR("mqtt_publish: total length overflow", (total_len <= 0xFFFF), return ERR_ARG);
+  remaining_length = (u16_t)total_len;
+
+  LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_publish: Publish with payload length %d to topic \"%s\"\n", payload_length, topic));
+
+  if (qos > 0) {
+    remaining_length += 2;
+    /* Generate pkt_id id for QoS1 and 2 */
+    pkt_id = msg_generate_packet_id(client);
+  } else {
+    /* Use reserved value pkt_id 0 for QoS 0 in request handle */
+    pkt_id = 0;
+  }
+
+  r = mqtt_create_request(client->req_list, pkt_id, cb, arg);
+  if (r == NULL) {
+    return ERR_MEM;
+  }
+
+  if (mqtt_output_check_space(&client->output, remaining_length) == 0) {
+    mqtt_delete_request(r);
+    return ERR_MEM;
+  }
+  /* Append fixed header */
+  mqtt_output_append_fixed_header(&client->output, MQTT_MSG_TYPE_PUBLISH, 0, qos, retain, remaining_length);
+
+  /* Append Topic */
+  mqtt_output_append_string(&client->output, topic, topic_len);
+
+  /* Append packet if for QoS 1 and 2*/
+  if (qos > 0) {
+    mqtt_output_append_u16(&client->output, pkt_id);
+  }
+
+  /* Append optional publish payload */
+  if ((payload != NULL) && (payload_length > 0)) {
+    mqtt_output_append_buf(&client->output, payload, payload_length);
+  }
+
+  mqtt_append_request(&client->pend_req_queue, r);
+  mqtt_output_send(&client->output, client->conn);
+  return ERR_OK;
+}
+
+
+/**
+ * @ingroup mqtt
+ * MQTT subscribe/unsubscribe function.
+ * @param client MQTT client
+ * @param topic topic to subscribe to
+ * @param qos Quality of service, 0 1 or 2 (only used for subscribe)
+ * @param cb Callback to call when subscribe/unsubscribe reponse is received
+ * @param arg User supplied argument to publish callback
+ * @param sub 1 for subscribe, 0 for unsubscribe
+ * @return ERR_OK if successful, @see err_t enum for other results
+ */
+err_t
+mqtt_sub_unsub(mqtt_client_t *client, const char *topic, u8_t qos, mqtt_request_cb_t cb, void *arg, u8_t sub)
+{
+  size_t topic_strlen;
+  size_t total_len;
+  u16_t topic_len;
+  u16_t remaining_length;
+  u16_t pkt_id;
+  struct mqtt_request_t *r;
+
+  LWIP_ASSERT("mqtt_sub_unsub: client != NULL", client);
+  LWIP_ASSERT("mqtt_sub_unsub: topic != NULL", topic);
+
+  topic_strlen = strlen(topic);
+  LWIP_ERROR("mqtt_sub_unsub: topic length overflow", (topic_strlen <= (0xFFFF - 2)), return ERR_ARG);
+  topic_len = (u16_t)topic_strlen;
+  /* Topic string, pkt_id, qos for subscribe */
+  total_len =  topic_len + 2 + 2 + (sub != 0);
+  LWIP_ERROR("mqtt_sub_unsub: total length overflow", (total_len <= 0xFFFF), return ERR_ARG);
+  remaining_length = (u16_t)total_len;
+
+  LWIP_ASSERT("mqtt_sub_unsub: qos < 3", qos < 3);
+  if (client->conn_state == TCP_DISCONNECTED) {
+    LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_sub_unsub: Can not (un)subscribe in disconnected state\n"));
+    return ERR_CONN;
+  }
+
+  pkt_id = msg_generate_packet_id(client);
+  r = mqtt_create_request(client->req_list, pkt_id, cb, arg);
+  if (r == NULL) {
+    return ERR_MEM;
+  }
+
+  if (mqtt_output_check_space(&client->output, remaining_length) == 0) {
+    mqtt_delete_request(r);
+    return ERR_MEM;
+  }
+
+  LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_sub_unsub: Client (un)subscribe to topic \"%s\", id: %d\n", topic, pkt_id));
+
+  mqtt_output_append_fixed_header(&client->output, sub ? MQTT_MSG_TYPE_SUBSCRIBE : MQTT_MSG_TYPE_UNSUBSCRIBE, 0, 1, 0, remaining_length);
+  /* Packet id */
+  mqtt_output_append_u16(&client->output, pkt_id);
+  /* Topic */
+  mqtt_output_append_string(&client->output, topic, topic_len);
+  /* QoS */
+  if (sub != 0) {
+    mqtt_output_append_u8(&client->output, LWIP_MIN(qos, 2));
+  }
+
+  mqtt_append_request(&client->pend_req_queue, r);
+  mqtt_output_send(&client->output, client->conn);
+  return ERR_OK;
+}
+
+
+/**
+ * @ingroup mqtt
+ * Set callback to handle incoming publish requests from server
+ * @param client MQTT client
+ * @param pub_cb Callback invoked when publish starts, contain topic and total length of payload
+ * @param data_cb Callback for each fragment of payload that arrives
+ * @param arg User supplied argument to both callbacks
+ */
+void
+mqtt_set_inpub_callback(mqtt_client_t *client, mqtt_incoming_publish_cb_t pub_cb,
+                             mqtt_incoming_data_cb_t data_cb, void *arg)
+{
+  LWIP_ASSERT("mqtt_set_inpub_callback: client != NULL", client != NULL);
+  client->data_cb = data_cb;
+  client->pub_cb = pub_cb;
+  client->inpub_arg = arg;
+}
+
+/**
+ * @ingroup mqtt
+ * Create a new MQTT client instance
+ * @return Pointer to instance on success, NULL otherwise
+ */
+mqtt_client_t *
+mqtt_client_new(void)
+{
+  mqtt_client_t *client = (mqtt_client_t *)mem_malloc(sizeof(mqtt_client_t));
+  if (client != NULL) {
+    memset(client, 0, sizeof(mqtt_client_t));
+  }
+  return client;
+}
+
+
+/**
+ * @ingroup mqtt
+ * Connect to MQTT server
+ * @param client MQTT client
+ * @param ip_addr Server IP
+ * @param port Server port
+ * @param cb Connection state change callback
+ * @param arg User supplied argument to connection callback
+ * @param client_info Client identification and connection options
+ * @return ERR_OK if successful, @see err_t enum for other results
+ */
+err_t
+mqtt_client_connect(mqtt_client_t *client, const ip_addr_t *ip_addr, u16_t port, mqtt_connection_cb_t cb, void *arg,
+                    const struct mqtt_connect_client_info_t *client_info)
+{
+  err_t err;
+  size_t len;
+  u16_t client_id_length;
+  /* Length is the sum of 2+"MQTT", protocol level, flags and keep alive */
+  u16_t remaining_length = 2 + 4 + 1 + 1 + 2;
+  u8_t flags = 0, will_topic_len = 0, will_msg_len = 0;
+  u16_t client_user_len = 0, client_pass_len = 0;
+
+  LWIP_ASSERT("mqtt_client_connect: client != NULL", client != NULL);
+  LWIP_ASSERT("mqtt_client_connect: ip_addr != NULL", ip_addr != NULL);
+  LWIP_ASSERT("mqtt_client_connect: client_info != NULL", client_info != NULL);
+  LWIP_ASSERT("mqtt_client_connect: client_info->client_id != NULL", client_info->client_id != NULL);
+
+  if (client->conn_state != TCP_DISCONNECTED) {
+    LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_client_connect: Already connected\n"));
+    return ERR_ISCONN;
+  }
+
+  /* Wipe clean */
+  memset(client, 0, sizeof(mqtt_client_t));
+  client->connect_arg = arg;
+  client->connect_cb = cb;
+  client->keep_alive = client_info->keep_alive;
+  mqtt_init_requests(client->req_list);
+
+  /* Build connect message */
+  if (client_info->will_topic != NULL && client_info->will_msg != NULL) {
+    flags |= MQTT_CONNECT_FLAG_WILL;
+    flags |= (client_info->will_qos & 3) << 3;
+    if (client_info->will_retain) {
+      flags |= MQTT_CONNECT_FLAG_WILL_RETAIN;
+    }
+    len = strlen(client_info->will_topic);
+    LWIP_ERROR("mqtt_client_connect: client_info->will_topic length overflow", len <= 0xFF, return ERR_VAL);
+    LWIP_ERROR("mqtt_client_connect: client_info->will_topic length must be > 0", len > 0, return ERR_VAL);
+    will_topic_len = (u8_t)len;
+    len = strlen(client_info->will_msg);
+    LWIP_ERROR("mqtt_client_connect: client_info->will_msg length overflow", len <= 0xFF, return ERR_VAL);
+    will_msg_len = (u8_t)len;
+    len = remaining_length + 2 + will_topic_len + 2 + will_msg_len;
+    LWIP_ERROR("mqtt_client_connect: remaining_length overflow", len <= 0xFFFF, return ERR_VAL);
+    remaining_length = (u16_t)len;
+  }
+  if (client_info->client_user != NULL) {
+    flags |= MQTT_CONNECT_FLAG_USERNAME;
+    len = strlen(client_info->client_user);
+    LWIP_ERROR("mqtt_client_connect: client_info->client_user length overflow", len <= 0xFFFF, return ERR_VAL);
+    LWIP_ERROR("mqtt_client_connect: client_info->client_user length must be > 0", len > 0, return ERR_VAL);
+    client_user_len = (u16_t)len;
+    len = remaining_length + 2 + client_user_len;
+    LWIP_ERROR("mqtt_client_connect: remaining_length overflow", len <= 0xFFFF, return ERR_VAL);
+    remaining_length = (u16_t)len;
+  }
+  if (client_info->client_pass != NULL) {
+    flags |= MQTT_CONNECT_FLAG_PASSWORD;
+    len = strlen(client_info->client_pass);
+    LWIP_ERROR("mqtt_client_connect: client_info->client_pass length overflow", len <= 0xFFFF, return ERR_VAL);
+    LWIP_ERROR("mqtt_client_connect: client_info->client_pass length must be > 0", len > 0, return ERR_VAL);
+    client_pass_len = (u16_t)len;
+    len = remaining_length + 2 + client_pass_len;
+    LWIP_ERROR("mqtt_client_connect: remaining_length overflow", len <= 0xFFFF, return ERR_VAL);
+    remaining_length = (u16_t)len;
+  }
+
+  /* Don't complicate things, always connect using clean session */
+  flags |= MQTT_CONNECT_FLAG_CLEAN_SESSION;
+
+  len = strlen(client_info->client_id);
+  LWIP_ERROR("mqtt_client_connect: client_info->client_id length overflow", len <= 0xFFFF, return ERR_VAL);
+  client_id_length = (u16_t)len;
+  len = remaining_length + 2 + client_id_length;
+  LWIP_ERROR("mqtt_client_connect: remaining_length overflow", len <= 0xFFFF, return ERR_VAL);
+  remaining_length = (u16_t)len;
+
+  if (mqtt_output_check_space(&client->output, remaining_length) == 0) {
+    return ERR_MEM;
+  }
+
+  client->conn = tcp_new();
+  if (client->conn == NULL) {
+    return ERR_MEM;
+  }
+
+  /* Set arg pointer for callbacks */
+  tcp_arg(client->conn, client);
+  /* Any local address, pick random local port number */
+  err = tcp_bind(client->conn, IP_ADDR_ANY, 0);
+  if (err != ERR_OK) {
+    LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_client_connect: Error binding to local ip/port, %d\n", err));
+    goto tcp_fail;
+  }
+  LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_client_connect: Connecting to host: %s at port:%"U16_F"\n", ipaddr_ntoa(ip_addr), port));
+
+  /* Connect to server */
+  err = tcp_connect(client->conn, ip_addr, port, mqtt_tcp_connect_cb);
+  if (err != ERR_OK) {
+    LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_client_connect: Error connecting to remote ip/port, %d\n", err));
+    goto tcp_fail;
+  }
+  /* Set error callback */
+  tcp_err(client->conn, mqtt_tcp_err_cb);
+  client->conn_state = TCP_CONNECTING;
+
+  /* Append fixed header */
+  mqtt_output_append_fixed_header(&client->output, MQTT_MSG_TYPE_CONNECT, 0, 0, 0, remaining_length);
+  /* Append Protocol string */
+  mqtt_output_append_string(&client->output, "MQTT", 4);
+  /* Append Protocol level */
+  mqtt_output_append_u8(&client->output, 4);
+  /* Append connect flags */
+  mqtt_output_append_u8(&client->output, flags);
+  /* Append keep-alive */
+  mqtt_output_append_u16(&client->output, client_info->keep_alive);
+  /* Append client id */
+  mqtt_output_append_string(&client->output, client_info->client_id, client_id_length);
+  /* Append will message if used */
+  if ((flags & MQTT_CONNECT_FLAG_WILL) != 0) {
+    mqtt_output_append_string(&client->output, client_info->will_topic, will_topic_len);
+    mqtt_output_append_string(&client->output, client_info->will_msg, will_msg_len);
+  }
+  /* Append user name if given */
+  if ((flags & MQTT_CONNECT_FLAG_USERNAME) != 0) {
+    mqtt_output_append_string(&client->output, client_info->client_user, client_user_len);
+  }
+  /* Append password if given */
+  if ((flags & MQTT_CONNECT_FLAG_PASSWORD) != 0) {
+    mqtt_output_append_string(&client->output, client_info->client_pass, client_pass_len);
+  }
+  return ERR_OK;
+
+tcp_fail:
+  tcp_abort(client->conn);
+  client->conn = NULL;
+  return err;
+}
+
+
+/**
+ * @ingroup mqtt
+ * Disconnect from MQTT server
+ * @param client MQTT client
+ */
+void
+mqtt_disconnect(mqtt_client_t *client)
+{
+  LWIP_ASSERT("mqtt_disconnect: client != NULL", client);
+  /* If connection in not already closed */
+  if (client->conn_state != TCP_DISCONNECTED) {
+    /* Set conn_state before calling mqtt_close to prevent callback from being called */
+    client->conn_state = TCP_DISCONNECTED;
+    mqtt_close(client, (mqtt_connection_status_t)0);
+  }
+}
+
+/**
+ * @ingroup mqtt
+ * Check connection with server
+ * @param client MQTT client
+ * @return 1 if connected to server, 0 otherwise
+ */
+u8_t
+mqtt_client_is_connected(mqtt_client_t *client)
+{
+  LWIP_ASSERT("mqtt_client_is_connected: client != NULL", client);
+  return client->conn_state == MQTT_CONNECTED;
+}
+
+#endif /* LWIP_TCP && LWIP_CALLBACK_API */

+ 367 - 0
libs/thirdparty/LwIP/src/apps/netbiosns/netbiosns.c

@@ -0,0 +1,367 @@
+/**
+ * @file
+ * NetBIOS name service responder
+ */
+
+/**
+ * @defgroup netbiosns NETBIOS responder
+ * @ingroup apps
+ *
+ * This is an example implementation of a NetBIOS name server.
+ * It responds to name queries for a configurable name.
+ * Name resolving is not supported.
+ *
+ * Note that the device doesn't broadcast it's own name so can't
+ * detect duplicate names!
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#include "lwip/apps/netbiosns.h"
+
+#if LWIP_IPV4 && LWIP_UDP  /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/def.h"
+#include "lwip/udp.h"
+#include "lwip/netif.h"
+
+#include <string.h>
+
+/** default port number for "NetBIOS Name service */
+#define NETBIOS_PORT     137
+
+/** size of a NetBIOS name */
+#define NETBIOS_NAME_LEN 16
+
+/** The Time-To-Live for NetBIOS name responds (in seconds)
+ * Default is 300000 seconds (3 days, 11 hours, 20 minutes) */
+#define NETBIOS_NAME_TTL 300000u
+
+/** NetBIOS header flags */
+#define NETB_HFLAG_RESPONSE           0x8000U
+#define NETB_HFLAG_OPCODE             0x7800U
+#define NETB_HFLAG_OPCODE_NAME_QUERY  0x0000U
+#define NETB_HFLAG_AUTHORATIVE        0x0400U
+#define NETB_HFLAG_TRUNCATED          0x0200U
+#define NETB_HFLAG_RECURS_DESIRED     0x0100U
+#define NETB_HFLAG_RECURS_AVAILABLE   0x0080U
+#define NETB_HFLAG_BROADCAST          0x0010U
+#define NETB_HFLAG_REPLYCODE          0x0008U
+#define NETB_HFLAG_REPLYCODE_NOERROR  0x0000U
+
+/** NetBIOS name flags */
+#define NETB_NFLAG_UNIQUE             0x8000U
+#define NETB_NFLAG_NODETYPE           0x6000U
+#define NETB_NFLAG_NODETYPE_HNODE     0x6000U
+#define NETB_NFLAG_NODETYPE_MNODE     0x4000U
+#define NETB_NFLAG_NODETYPE_PNODE     0x2000U
+#define NETB_NFLAG_NODETYPE_BNODE     0x0000U
+
+/** NetBIOS message header */
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct netbios_hdr {
+  PACK_STRUCT_FIELD(u16_t trans_id);
+  PACK_STRUCT_FIELD(u16_t flags);
+  PACK_STRUCT_FIELD(u16_t questions);
+  PACK_STRUCT_FIELD(u16_t answerRRs);
+  PACK_STRUCT_FIELD(u16_t authorityRRs);
+  PACK_STRUCT_FIELD(u16_t additionalRRs);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+/** NetBIOS message name part */
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct netbios_name_hdr {
+  PACK_STRUCT_FLD_8(u8_t  nametype);
+  PACK_STRUCT_FLD_8(u8_t  encname[(NETBIOS_NAME_LEN*2)+1]);
+  PACK_STRUCT_FIELD(u16_t type);
+  PACK_STRUCT_FIELD(u16_t cls);
+  PACK_STRUCT_FIELD(u32_t ttl);
+  PACK_STRUCT_FIELD(u16_t datalen);
+  PACK_STRUCT_FIELD(u16_t flags);
+  PACK_STRUCT_FLD_S(ip4_addr_p_t addr);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+/** NetBIOS message */
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct netbios_resp
+{
+  struct netbios_hdr      resp_hdr;
+  struct netbios_name_hdr resp_name;
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+#ifdef NETBIOS_LWIP_NAME
+#define NETBIOS_LOCAL_NAME NETBIOS_LWIP_NAME
+#else
+static char netbiosns_local_name[NETBIOS_NAME_LEN];
+#define NETBIOS_LOCAL_NAME netbiosns_local_name
+#endif
+
+struct udp_pcb *netbiosns_pcb;
+
+/** Decode a NetBIOS name (from packet to string) */
+static int
+netbiosns_name_decode(char *name_enc, char *name_dec, int name_dec_len)
+{
+  char *pname;
+  char  cname;
+  char  cnbname;
+  int   idx = 0;
+
+  LWIP_UNUSED_ARG(name_dec_len);
+
+  /* Start decoding netbios name. */
+  pname  = name_enc;
+  for (;;) {
+    /* Every two characters of the first level-encoded name
+     * turn into one character in the decoded name. */
+    cname = *pname;
+    if (cname == '\0')
+      break;    /* no more characters */
+    if (cname == '.')
+      break;    /* scope ID follows */
+    if (cname < 'A' || cname > 'Z') {
+      /* Not legal. */
+      return -1;
+    }
+    cname -= 'A';
+    cnbname = cname << 4;
+    pname++;
+
+    cname = *pname;
+    if (cname == '\0' || cname == '.') {
+      /* No more characters in the name - but we're in
+       * the middle of a pair.  Not legal. */
+      return -1;
+    }
+    if (cname < 'A' || cname > 'Z') {
+      /* Not legal. */
+      return -1;
+    }
+    cname -= 'A';
+    cnbname |= cname;
+    pname++;
+
+    /* Do we have room to store the character? */
+    if (idx < NETBIOS_NAME_LEN) {
+      /* Yes - store the character. */
+      name_dec[idx++] = (cnbname!=' '?cnbname:'\0');
+    }
+  }
+
+  return 0;
+}
+
+#if 0 /* function currently unused */
+/** Encode a NetBIOS name (from string to packet) - currently unused because
+    we don't ask for names. */
+static int
+netbiosns_name_encode(char *name_enc, char *name_dec, int name_dec_len)
+{
+  char         *pname;
+  char          cname;
+  unsigned char ucname;
+  int           idx = 0;
+
+  /* Start encoding netbios name. */
+  pname = name_enc;
+
+  for (;;) {
+    /* Every two characters of the first level-encoded name
+     * turn into one character in the decoded name. */
+    cname = *pname;
+    if (cname == '\0')
+      break;    /* no more characters */
+    if (cname == '.')
+      break;    /* scope ID follows */
+    if ((cname < 'A' || cname > 'Z') && (cname < '0' || cname > '9')) {
+      /* Not legal. */
+      return -1;
+    }
+
+    /* Do we have room to store the character? */
+    if (idx >= name_dec_len) {
+      return -1;
+    }
+
+    /* Yes - store the character. */
+    ucname = cname;
+    name_dec[idx++] = ('A'+((ucname>>4) & 0x0F));
+    name_dec[idx++] = ('A'+( ucname     & 0x0F));
+    pname++;
+  }
+
+  /* Fill with "space" coding */
+  for (;idx < name_dec_len - 1;) {
+    name_dec[idx++] = 'C';
+    name_dec[idx++] = 'A';
+  }
+
+  /* Terminate string */
+  name_dec[idx] = '\0';
+
+  return 0;
+}
+#endif /* 0 */
+
+/** NetBIOS Name service recv callback */
+static void
+netbiosns_recv(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
+{
+  LWIP_UNUSED_ARG(arg);
+
+  /* if packet is valid */
+  if (p != NULL) {
+    char   netbios_name[NETBIOS_NAME_LEN+1];
+    struct netbios_hdr*      netbios_hdr      = (struct netbios_hdr*)p->payload;
+    struct netbios_name_hdr* netbios_name_hdr = (struct netbios_name_hdr*)(netbios_hdr+1);
+
+    /* we only answer if we got a default interface */
+    if (netif_default != NULL) {
+      /* @todo: do we need to check answerRRs/authorityRRs/additionalRRs? */
+      /* if the packet is a NetBIOS name query question */
+      if (((netbios_hdr->flags & PP_NTOHS(NETB_HFLAG_OPCODE)) == PP_NTOHS(NETB_HFLAG_OPCODE_NAME_QUERY)) &&
+          ((netbios_hdr->flags & PP_NTOHS(NETB_HFLAG_RESPONSE)) == 0) &&
+           (netbios_hdr->questions == PP_NTOHS(1))) {
+        /* decode the NetBIOS name */
+        netbiosns_name_decode((char*)(netbios_name_hdr->encname), netbios_name, sizeof(netbios_name));
+        /* if the packet is for us */
+        if (lwip_strnicmp(netbios_name, NETBIOS_LOCAL_NAME, sizeof(NETBIOS_LOCAL_NAME)) == 0) {
+          struct pbuf *q;
+          struct netbios_resp *resp;
+
+          q = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct netbios_resp), PBUF_RAM);
+          if (q != NULL) {
+            resp = (struct netbios_resp*)q->payload;
+
+            /* prepare NetBIOS header response */
+            resp->resp_hdr.trans_id      = netbios_hdr->trans_id;
+            resp->resp_hdr.flags         = PP_HTONS(NETB_HFLAG_RESPONSE |
+                                                 NETB_HFLAG_OPCODE_NAME_QUERY |
+                                                 NETB_HFLAG_AUTHORATIVE |
+                                                 NETB_HFLAG_RECURS_DESIRED);
+            resp->resp_hdr.questions     = 0;
+            resp->resp_hdr.answerRRs     = PP_HTONS(1);
+            resp->resp_hdr.authorityRRs  = 0;
+            resp->resp_hdr.additionalRRs = 0;
+
+            /* prepare NetBIOS header datas */
+            MEMCPY( resp->resp_name.encname, netbios_name_hdr->encname, sizeof(netbios_name_hdr->encname));
+            resp->resp_name.nametype     = netbios_name_hdr->nametype;
+            resp->resp_name.type         = netbios_name_hdr->type;
+            resp->resp_name.cls          = netbios_name_hdr->cls;
+            resp->resp_name.ttl          = PP_HTONL(NETBIOS_NAME_TTL);
+            resp->resp_name.datalen      = PP_HTONS(sizeof(resp->resp_name.flags)+sizeof(resp->resp_name.addr));
+            resp->resp_name.flags        = PP_HTONS(NETB_NFLAG_NODETYPE_BNODE);
+            ip4_addr_copy(resp->resp_name.addr, *netif_ip4_addr(netif_default));
+
+            /* send the NetBIOS response */
+            udp_sendto(upcb, q, addr, port);
+
+            /* free the "reference" pbuf */
+            pbuf_free(q);
+          }
+        }
+      }
+    }
+    /* free the pbuf */
+    pbuf_free(p);
+  }
+}
+
+/**
+ * @ingroup netbiosns 
+ * Init netbios responder
+ */
+void
+netbiosns_init(void)
+{
+#ifdef NETBIOS_LWIP_NAME
+  LWIP_ASSERT("NetBIOS name is too long!", strlen(NETBIOS_LWIP_NAME) < NETBIOS_NAME_LEN);
+#endif
+
+  netbiosns_pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
+  if (netbiosns_pcb != NULL) {
+    /* we have to be allowed to send broadcast packets! */
+    ip_set_option(netbiosns_pcb, SOF_BROADCAST);
+    udp_bind(netbiosns_pcb, IP_ANY_TYPE, NETBIOS_PORT);
+    udp_recv(netbiosns_pcb, netbiosns_recv, netbiosns_pcb);
+  }
+}
+
+#ifndef NETBIOS_LWIP_NAME
+/**
+ * @ingroup netbiosns 
+ * Set netbios name. ATTENTION: the hostname must be less than 15 characters!
+ */
+void
+netbiosns_set_name(const char* hostname)
+{
+  size_t copy_len = strlen(hostname);
+  LWIP_ASSERT("NetBIOS name is too long!", copy_len < NETBIOS_NAME_LEN);
+  if (copy_len >= NETBIOS_NAME_LEN) {
+    copy_len = NETBIOS_NAME_LEN - 1;
+  }
+  MEMCPY(netbiosns_local_name, hostname, copy_len + 1);
+}
+#endif
+
+/**
+ * @ingroup netbiosns 
+ * Stop netbios responder
+ */
+void
+netbiosns_stop(void)
+{
+  if (netbiosns_pcb != NULL) {
+    udp_remove(netbiosns_pcb);
+    netbiosns_pcb = NULL;
+  }
+}
+
+#endif /* LWIP_IPV4 && LWIP_UDP */

+ 749 - 704
libs/thirdparty/lwip_2.1.2/src/apps/snmp/snmp_asn1.c → libs/thirdparty/LwIP/src/apps/snmp/snmp_asn1.c

@@ -1,704 +1,749 @@
-/**
- * @file
- * Abstract Syntax Notation One (ISO 8824, 8825) encoding
- *
- * @todo not optimised (yet), favor correctness over speed, favor speed over size
- */
-
-/*
- * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * Author: Christiaan Simons <christiaan.simons@axon.tv>
- *         Martin Hentschel <info@cl-soft.de>
- */
-
-#include "lwip/apps/snmp_opts.h"
-
-#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
-
-#include "snmp_asn1.h"
-
-#define PBUF_OP_EXEC(code) \
-  if ((code) != ERR_OK) { \
-    return ERR_BUF; \
-  }
-
-/**
- * Encodes a TLV into a pbuf stream.
- *
- * @param pbuf_stream points to a pbuf stream
- * @param tlv TLV to encode
- * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
- */
-err_t
-snmp_ans1_enc_tlv(struct snmp_pbuf_stream *pbuf_stream, struct snmp_asn1_tlv *tlv)
-{
-  u8_t data;
-  u8_t length_bytes_required;
-
-  /* write type */
-  if ((tlv->type & SNMP_ASN1_DATATYPE_MASK) == SNMP_ASN1_DATATYPE_EXTENDED) {
-    /* extended format is not used by SNMP so we do not accept those values */
-    return ERR_ARG;
-  }
-  if (tlv->type_len != 0) {
-    /* any other value as auto is not accepted for type (we always use one byte because extended syntax is prohibited) */
-    return ERR_ARG;
-  }
-
-  PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, tlv->type));
-  tlv->type_len = 1;
-
-  /* write length */
-  if (tlv->value_len <= 127) {
-    length_bytes_required = 1;
-  } else if (tlv->value_len <= 255) {
-    length_bytes_required = 2;
-  } else  {
-    length_bytes_required = 3;
-  }
-
-  /* check for forced min length */
-  if (tlv->length_len > 0) {
-    if (tlv->length_len < length_bytes_required) {
-      /* unable to code requested length in requested number of bytes */
-      return ERR_ARG;
-    }
-
-    length_bytes_required = tlv->length_len;
-  } else {
-    tlv->length_len = length_bytes_required;
-  }
-
-  if (length_bytes_required > 1) {
-    /* multi byte representation required */
-    length_bytes_required--;
-    data = 0x80 | length_bytes_required; /* extended length definition, 1 length byte follows */
-
-    PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data));
-
-    while (length_bytes_required > 1) {
-      if (length_bytes_required == 2) {
-        /* append high byte */
-        data = (u8_t)(tlv->value_len >> 8);
-      } else {
-        /* append leading 0x00 */
-        data = 0x00;
-      }
-
-      PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data));
-      length_bytes_required--;
-    }
-  }
-
-  /* append low byte */
-  data = (u8_t)(tlv->value_len & 0xFF);
-  PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data));
-
-  return ERR_OK;
-}
-
-/**
- * Encodes raw data (octet string, opaque) into a pbuf chained ASN1 msg.
- *
- * @param pbuf_stream points to a pbuf stream
- * @param raw_len raw data length
- * @param raw points raw data
- * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
- */
-err_t
-snmp_asn1_enc_raw(struct snmp_pbuf_stream *pbuf_stream, const u8_t *raw, u16_t raw_len)
-{
-  PBUF_OP_EXEC(snmp_pbuf_stream_writebuf(pbuf_stream, raw, raw_len));
-
-  return ERR_OK;
-}
-
-/**
- * Encodes u32_t (counter, gauge, timeticks) into a pbuf chained ASN1 msg.
- *
- * @param pbuf_stream points to a pbuf stream
- * @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt())
- * @param value is the host order u32_t value to be encoded
- * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
- *
- * @see snmp_asn1_enc_u32t_cnt()
- */
-err_t
-snmp_asn1_enc_u32t(struct snmp_pbuf_stream *pbuf_stream, u16_t octets_needed, u32_t value)
-{
-  if (octets_needed > 5) {
-    return ERR_ARG;
-  }
-  if (octets_needed == 5) {
-    /* not enough bits in 'value' add leading 0x00 */
-    PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, 0x00));
-    octets_needed--;
-  }
-
-  while (octets_needed > 1) {
-    octets_needed--;
-    PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value >> (octets_needed << 3))));
-  }
-
-  /* (only) one least significant octet */
-  PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)value));
-
-  return ERR_OK;
-}
-/**
- * Encodes s32_t integer into a pbuf chained ASN1 msg.
- *
- * @param pbuf_stream points to a pbuf stream
- * @param octets_needed encoding length (from snmp_asn1_enc_s32t_cnt())
- * @param value is the host order s32_t value to be encoded
- * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
- *
- * @see snmp_asn1_enc_s32t_cnt()
- */
-err_t
-snmp_asn1_enc_s32t(struct snmp_pbuf_stream *pbuf_stream, u16_t octets_needed, s32_t value)
-{
-  while (octets_needed > 1) {
-    octets_needed--;
-
-    PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value >> (octets_needed << 3))));
-  }
-
-  /* (only) one least significant octet */
-  PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)value));
-
-  return ERR_OK;
-}
-
-/**
- * Encodes object identifier into a pbuf chained ASN1 msg.
- *
- * @param pbuf_stream points to a pbuf stream
- * @param oid points to object identifier array
- * @param oid_len object identifier array length
- * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
- */
-err_t
-snmp_asn1_enc_oid(struct snmp_pbuf_stream *pbuf_stream, const u32_t *oid, u16_t oid_len)
-{
-  if (oid_len > 1) {
-    /* write compressed first two sub id's */
-    u32_t compressed_byte = ((oid[0] * 40) + oid[1]);
-    PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)compressed_byte));
-    oid_len -= 2;
-    oid += 2;
-  } else {
-    /* @bug:  allow empty varbinds for symmetry (we must decode them for getnext), allow partial compression?? */
-    /* ident_len <= 1, at least we need zeroDotZero (0.0) (ident_len == 2) */
-    return ERR_ARG;
-  }
-
-  while (oid_len > 0) {
-    u32_t sub_id;
-    u8_t shift, tail;
-
-    oid_len--;
-    sub_id = *oid;
-    tail = 0;
-    shift = 28;
-    while (shift > 0) {
-      u8_t code;
-
-      code = (u8_t)(sub_id >> shift);
-      if ((code != 0) || (tail != 0)) {
-        tail = 1;
-        PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, code | 0x80));
-      }
-      shift -= 7;
-    }
-    PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)sub_id & 0x7F));
-
-    /* proceed to next sub-identifier */
-    oid++;
-  }
-  return ERR_OK;
-}
-
-/**
- * Returns octet count for length.
- *
- * @param length parameter length
- * @param octets_needed points to the return value
- */
-void
-snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed)
-{
-  if (length < 0x80U) {
-    *octets_needed = 1;
-  } else if (length < 0x100U) {
-    *octets_needed = 2;
-  } else {
-    *octets_needed = 3;
-  }
-}
-
-/**
- * Returns octet count for an u32_t.
- *
- * @param value value to be encoded
- * @param octets_needed points to the return value
- *
- * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
- * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
- * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
- */
-void
-snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed)
-{
-  if (value < 0x80UL) {
-    *octets_needed = 1;
-  } else if (value < 0x8000UL) {
-    *octets_needed = 2;
-  } else if (value < 0x800000UL) {
-    *octets_needed = 3;
-  } else if (value < 0x80000000UL) {
-    *octets_needed = 4;
-  } else {
-    *octets_needed = 5;
-  }
-}
-
-/**
- * Returns octet count for an s32_t.
- *
- * @param value value to be encoded
- * @param octets_needed points to the return value
- *
- * @note ASN coded integers are _always_ signed.
- */
-void
-snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed)
-{
-  if (value < 0) {
-    value = ~value;
-  }
-  if (value < 0x80L) {
-    *octets_needed = 1;
-  } else if (value < 0x8000L) {
-    *octets_needed = 2;
-  } else if (value < 0x800000L) {
-    *octets_needed = 3;
-  } else {
-    *octets_needed = 4;
-  }
-}
-
-/**
- * Returns octet count for an object identifier.
- *
- * @param oid points to object identifier array
- * @param oid_len object identifier array length
- * @param octets_needed points to the return value
- */
-void
-snmp_asn1_enc_oid_cnt(const u32_t *oid, u16_t oid_len, u16_t *octets_needed)
-{
-  u32_t sub_id;
-
-  *octets_needed = 0;
-  if (oid_len > 1) {
-    /* compressed prefix in one octet */
-    (*octets_needed)++;
-    oid_len -= 2;
-    oid += 2;
-  }
-  while (oid_len > 0) {
-    oid_len--;
-    sub_id = *oid;
-
-    sub_id >>= 7;
-    (*octets_needed)++;
-    while (sub_id > 0) {
-      sub_id >>= 7;
-      (*octets_needed)++;
-    }
-    oid++;
-  }
-}
-
-/**
- * Decodes a TLV from a pbuf stream.
- *
- * @param pbuf_stream points to a pbuf stream
- * @param tlv returns decoded TLV
- * @return ERR_OK if successful, ERR_VAL if we can't decode
- */
-err_t
-snmp_asn1_dec_tlv(struct snmp_pbuf_stream *pbuf_stream, struct snmp_asn1_tlv *tlv)
-{
-  u8_t data;
-
-  /* decode type first */
-  PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
-  tlv->type = data;
-
-  if ((tlv->type & SNMP_ASN1_DATATYPE_MASK) == SNMP_ASN1_DATATYPE_EXTENDED) {
-    /* extended format is not used by SNMP so we do not accept those values */
-    return ERR_VAL;
-  }
-  tlv->type_len = 1;
-
-  /* now, decode length */
-  PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
-
-  if (data < 0x80) { /* short form */
-    tlv->length_len = 1;
-    tlv->value_len  = data;
-  } else if (data > 0x80) { /* long form */
-    u8_t length_bytes = data - 0x80;
-    if (length_bytes > pbuf_stream->length) {
-      return ERR_VAL;
-    }
-    tlv->length_len = length_bytes + 1; /* this byte + defined number of length bytes following */
-    tlv->value_len = 0;
-
-    while (length_bytes > 0) {
-      /* we only support up to u16.maxvalue-1 (2 bytes) but have to accept leading zero bytes */
-      if (tlv->value_len > 0xFF) {
-        return ERR_VAL;
-      }
-      PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
-      tlv->value_len <<= 8;
-      tlv->value_len |= data;
-
-      /* take care for special value used for indefinite length */
-      if (tlv->value_len == 0xFFFF) {
-        return ERR_VAL;
-      }
-
-      length_bytes--;
-    }
-  } else { /* data == 0x80 indefinite length form */
-    /* (not allowed for SNMP; RFC 1157, 3.2.2) */
-    return ERR_VAL;
-  }
-
-  return ERR_OK;
-}
-
-/**
- * Decodes positive integer (counter, gauge, timeticks) into u32_t.
- *
- * @param pbuf_stream points to a pbuf stream
- * @param len length of the coded integer field
- * @param value return host order integer
- * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
- *
- * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
- * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
- * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
- */
-err_t
-snmp_asn1_dec_u32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value)
-{
-  u8_t data;
-
-  if ((len > 0) && (len <= 5)) {
-    PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
-
-    /* expecting sign bit to be zero, only unsigned please! */
-    if (((len == 5) && (data == 0x00)) || ((len < 5) && ((data & 0x80) == 0))) {
-      *value = data;
-      len--;
-
-      while (len > 0) {
-        PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
-        len--;
-
-        *value <<= 8;
-        *value |= data;
-      }
-
-      return ERR_OK;
-    }
-  }
-
-  return ERR_VAL;
-}
-
-/**
- * Decodes integer into s32_t.
- *
- * @param pbuf_stream points to a pbuf stream
- * @param len length of the coded integer field
- * @param value return host order integer
- * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
- *
- * @note ASN coded integers are _always_ signed!
- */
-err_t
-snmp_asn1_dec_s32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, s32_t *value)
-{
-  u8_t data;
-
-  if ((len > 0) && (len < 5)) {
-    PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
-
-    if (data & 0x80) {
-      /* negative, start from -1 */
-      *value = -1;
-      *value = (*value << 8) | data;
-    } else {
-      /* positive, start from 0 */
-      *value = data;
-    }
-    len--;
-    /* shift in the remaining value */
-    while (len > 0) {
-      PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
-      *value = (*value << 8) | data;
-      len--;
-    }
-    return ERR_OK;
-  }
-
-  return ERR_VAL;
-}
-
-/**
- * Decodes object identifier from incoming message into array of u32_t.
- *
- * @param pbuf_stream points to a pbuf stream
- * @param len length of the coded object identifier
- * @param oid return decoded object identifier
- * @param oid_len return decoded object identifier length
- * @param oid_max_len size of oid buffer
- * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
- */
-err_t
-snmp_asn1_dec_oid(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *oid, u8_t *oid_len, u8_t oid_max_len)
-{
-  u32_t *oid_ptr;
-  u8_t data;
-
-  *oid_len = 0;
-  oid_ptr = oid;
-  if (len > 0) {
-    if (oid_max_len < 2) {
-      return ERR_MEM;
-    }
-
-    PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
-    len--;
-
-    /* first compressed octet */
-    if (data == 0x2B) {
-      /* (most) common case 1.3 (iso.org) */
-      *oid_ptr = 1;
-      oid_ptr++;
-      *oid_ptr = 3;
-      oid_ptr++;
-    } else if (data < 40) {
-      *oid_ptr = 0;
-      oid_ptr++;
-      *oid_ptr = data;
-      oid_ptr++;
-    } else if (data < 80) {
-      *oid_ptr = 1;
-      oid_ptr++;
-      *oid_ptr = data - 40;
-      oid_ptr++;
-    } else {
-      *oid_ptr = 2;
-      oid_ptr++;
-      *oid_ptr = data - 80;
-      oid_ptr++;
-    }
-    *oid_len = 2;
-  } else {
-    /* accepting zero length identifiers e.g. for getnext operation. uncommon but valid */
-    return ERR_OK;
-  }
-
-  while ((len > 0) && (*oid_len < oid_max_len)) {
-    PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
-    len--;
-
-    if ((data & 0x80) == 0x00) {
-      /* sub-identifier uses single octet */
-      *oid_ptr = data;
-    } else {
-      /* sub-identifier uses multiple octets */
-      u32_t sub_id = (data & ~0x80);
-      while ((len > 0) && ((data & 0x80) != 0)) {
-        PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
-        len--;
-
-        sub_id = (sub_id << 7) + (data & ~0x80);
-      }
-
-      if ((data & 0x80) != 0) {
-        /* "more bytes following" bit still set at end of len */
-        return ERR_VAL;
-      }
-      *oid_ptr = sub_id;
-    }
-    oid_ptr++;
-    (*oid_len)++;
-  }
-
-  if (len > 0) {
-    /* OID to long to fit in our buffer */
-    return ERR_MEM;
-  }
-
-  return ERR_OK;
-}
-
-/**
- * Decodes (copies) raw data (ip-addresses, octet strings, opaque encoding)
- * from incoming message into array.
- *
- * @param pbuf_stream points to a pbuf stream
- * @param len length of the coded raw data (zero is valid, e.g. empty string!)
- * @param buf return raw bytes
- * @param buf_len returns length of the raw return value
- * @param buf_max_len buffer size
- * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
- */
-err_t
-snmp_asn1_dec_raw(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u8_t *buf, u16_t *buf_len, u16_t buf_max_len)
-{
-  if (len > buf_max_len) {
-    /* not enough dst space */
-    return ERR_MEM;
-  }
-  *buf_len = len;
-
-  while (len > 0) {
-    PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, buf));
-    buf++;
-    len--;
-  }
-
-  return ERR_OK;
-}
-
-#if LWIP_HAVE_INT64
-/**
- * Returns octet count for an u64_t.
- *
- * @param value value to be encoded
- * @param octets_needed points to the return value
- *
- * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
- * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
- * of 0xFFFFFFFFFFFFFFFF is preceded with 0x00 and the length is 9 octets!!
- */
-void
-snmp_asn1_enc_u64t_cnt(u64_t value, u16_t *octets_needed)
-{
-  /* check if high u32 is 0 */
-  if ((value >> 32) == 0) {
-    /* only low u32 is important */
-    snmp_asn1_enc_u32t_cnt((u32_t)value, octets_needed);
-  } else {
-    /* low u32 does not matter for length determination */
-    snmp_asn1_enc_u32t_cnt((u32_t)(value >> 32), octets_needed);
-    *octets_needed = *octets_needed + 4; /* add the 4 bytes of low u32 */
-  }
-}
-
-/**
- * Decodes large positive integer (counter64) into 2x u32_t.
- *
- * @param pbuf_stream points to a pbuf stream
- * @param len length of the coded integer field
- * @param value return 64 bit integer
- * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
- *
- * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
- * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
- * of 0xFFFFFFFFFFFFFFFF is preceded with 0x00 and the length is 9 octets!!
- */
-err_t
-snmp_asn1_dec_u64t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u64_t *value)
-{
-  u8_t data;
-
-  if ((len > 0) && (len <= 9)) {
-    PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
-
-    /* expecting sign bit to be zero, only unsigned please! */
-    if (((len == 9) && (data == 0x00)) || ((len < 9) && ((data & 0x80) == 0))) {
-      *value = data;
-      len--;
-
-      while (len > 0) {
-        PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
-        *value <<= 8;
-        *value |= data;
-        len--;
-      }
-
-      return ERR_OK;
-    }
-  }
-
-  return ERR_VAL;
-}
-
-/**
- * Encodes u64_t (counter64) into a pbuf chained ASN1 msg.
- *
- * @param pbuf_stream points to a pbuf stream
- * @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt())
- * @param value is the value to be encoded
- * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
- *
- * @see snmp_asn1_enc_u64t_cnt()
- */
-err_t
-snmp_asn1_enc_u64t(struct snmp_pbuf_stream *pbuf_stream, u16_t octets_needed, u64_t value)
-{
-  if (octets_needed > 9) {
-    return ERR_ARG;
-  }
-  if (octets_needed == 9) {
-    /* not enough bits in 'value' add leading 0x00 */
-    PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, 0x00));
-    octets_needed--;
-  }
-
-  while (octets_needed > 1) {
-    octets_needed--;
-    PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value >> (octets_needed << 3))));
-  }
-
-  /* always write at least one octet (also in case of value == 0) */
-  PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value)));
-
-  return ERR_OK;
-}
-#endif
-
-#endif /* LWIP_SNMP */
+/**
+ * @file
+ * Abstract Syntax Notation One (ISO 8824, 8825) encoding
+ *
+ * @todo not optimised (yet), favor correctness over speed, favor speed over size
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Christiaan Simons <christiaan.simons@axon.tv>
+ *         Martin Hentschel <info@cl-soft.de>
+ */
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "snmp_asn1.h"
+
+#define PBUF_OP_EXEC(code) \
+  if ((code) != ERR_OK) { \
+    return ERR_BUF; \
+  }
+
+/**
+ * Encodes a TLV into a pbuf stream.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param tlv TLV to encode
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
+ */ 
+err_t
+snmp_ans1_enc_tlv(struct snmp_pbuf_stream* pbuf_stream, struct snmp_asn1_tlv* tlv)
+{
+  u8_t data;
+  u8_t length_bytes_required;
+
+  /* write type */
+  if ((tlv->type & SNMP_ASN1_DATATYPE_MASK) == SNMP_ASN1_DATATYPE_EXTENDED) {
+    /* extended format is not used by SNMP so we do not accept those values */
+    return ERR_ARG;
+  }
+  if (tlv->type_len != 0) {
+    /* any other value as auto is not accepted for type (we always use one byte because extended syntax is prohibited) */
+    return ERR_ARG;
+  }
+
+  PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, tlv->type));
+  tlv->type_len = 1;
+
+  /* write length */
+  if (tlv->value_len <= 127) {
+    length_bytes_required = 1;
+  } else if (tlv->value_len <= 255) {
+    length_bytes_required = 2;
+  } else  {
+    length_bytes_required = 3;
+  }
+
+  /* check for forced min length */
+  if (tlv->length_len > 0) {
+    if (tlv->length_len < length_bytes_required) {
+      /* unable to code requested length in requested number of bytes */
+      return ERR_ARG;
+    }
+
+    length_bytes_required = tlv->length_len;
+  } else {
+    tlv->length_len = length_bytes_required;
+  }
+
+  if (length_bytes_required > 1) {
+    /* multi byte representation required */
+    length_bytes_required--;
+    data = 0x80 | length_bytes_required; /* extended length definition, 1 length byte follows */
+
+    PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data));
+
+    while (length_bytes_required > 1) {
+      if (length_bytes_required == 2) {
+        /* append high byte */
+        data = (u8_t)(tlv->value_len >> 8);
+      } else {
+        /* append leading 0x00 */
+        data = 0x00;
+      }
+
+      PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data));
+      length_bytes_required--;
+    }
+  }
+
+  /* append low byte */
+  data = (u8_t)(tlv->value_len & 0xFF);
+  PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data));
+
+  return ERR_OK;
+}
+
+/**
+ * Encodes raw data (octet string, opaque) into a pbuf chained ASN1 msg.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param raw_len raw data length
+ * @param raw points raw data
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
+ */
+err_t
+snmp_asn1_enc_raw(struct snmp_pbuf_stream* pbuf_stream, const u8_t *raw, u16_t raw_len)
+{
+  PBUF_OP_EXEC(snmp_pbuf_stream_writebuf(pbuf_stream, raw, raw_len));
+
+  return ERR_OK;
+}
+
+/**
+ * Encodes u32_t (counter, gauge, timeticks) into a pbuf chained ASN1 msg.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt())
+ * @param value is the host order u32_t value to be encoded
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
+ *
+ * @see snmp_asn1_enc_u32t_cnt()
+ */
+err_t
+snmp_asn1_enc_u32t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, u32_t value)
+{
+  if (octets_needed > 5) {
+    return ERR_ARG;
+  }
+  if (octets_needed == 5) {
+    /* not enough bits in 'value' add leading 0x00 */
+    PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, 0x00));
+    octets_needed--;
+  }
+
+  while (octets_needed > 1) {
+    octets_needed--;
+    PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value >> (octets_needed << 3))));
+  }
+
+  /* (only) one least significant octet */
+  PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)value));
+
+  return ERR_OK;
+}
+
+/**
+ * Encodes u64_t (counter64) into a pbuf chained ASN1 msg.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt())
+ * @param value is the host order u32_t value to be encoded
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
+ *
+ * @see snmp_asn1_enc_u64t_cnt()
+ */
+err_t
+snmp_asn1_enc_u64t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, const u32_t* value)
+{
+  if (octets_needed > 9) {
+    return ERR_ARG;
+  }
+  if (octets_needed == 9) {
+    /* not enough bits in 'value' add leading 0x00 */
+    PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, 0x00));
+    octets_needed--;
+  }
+
+  while (octets_needed > 4) {
+    octets_needed--;
+    PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(*value >> ((octets_needed-4) << 3))));
+  }
+
+  /* skip to low u32 */
+  value++;
+
+  while (octets_needed > 1) {
+    octets_needed--;
+    PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(*value >> (octets_needed << 3))));
+  }
+
+  /* always write at least one octet (also in case of value == 0) */
+  PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(*value)));
+
+  return ERR_OK;
+}
+
+/**
+ * Encodes s32_t integer into a pbuf chained ASN1 msg.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param octets_needed encoding length (from snmp_asn1_enc_s32t_cnt())
+ * @param value is the host order s32_t value to be encoded
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
+ *
+ * @see snmp_asn1_enc_s32t_cnt()
+ */
+err_t
+snmp_asn1_enc_s32t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, s32_t value)
+{
+  while (octets_needed > 1) {
+    octets_needed--;
+
+    PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value >> (octets_needed << 3))));
+  }
+
+  /* (only) one least significant octet */
+  PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)value));
+
+  return ERR_OK;
+}
+
+/**
+ * Encodes object identifier into a pbuf chained ASN1 msg.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param oid points to object identifier array
+ * @param oid_len object identifier array length
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
+ */
+err_t
+snmp_asn1_enc_oid(struct snmp_pbuf_stream* pbuf_stream, const u32_t *oid, u16_t oid_len)
+{
+  if (oid_len > 1) {
+    /* write compressed first two sub id's */
+    u32_t compressed_byte = ((oid[0] * 40) + oid[1]);
+    PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)compressed_byte));
+    oid_len -= 2;
+    oid += 2;
+  } else {
+    /* @bug:  allow empty varbinds for symmetry (we must decode them for getnext), allow partial compression?? */
+    /* ident_len <= 1, at least we need zeroDotZero (0.0) (ident_len == 2) */
+    return ERR_ARG;
+  }
+
+  while (oid_len > 0) {
+    u32_t sub_id;
+    u8_t shift, tail;
+
+    oid_len--;
+    sub_id = *oid;
+    tail = 0;
+    shift = 28;
+    while (shift > 0) {
+      u8_t code;
+
+      code = (u8_t)(sub_id >> shift);
+      if ((code != 0) || (tail != 0)) {
+        tail = 1;
+        PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, code | 0x80));
+      }
+      shift -= 7;
+    }
+    PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)sub_id & 0x7F));
+
+    /* proceed to next sub-identifier */
+    oid++;
+  }
+  return ERR_OK;
+}
+
+/**
+ * Returns octet count for length.
+ *
+ * @param length parameter length
+ * @param octets_needed points to the return value
+ */
+void
+snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed)
+{
+  if (length < 0x80U) {
+    *octets_needed = 1;
+  } else if (length < 0x100U) {
+    *octets_needed = 2;
+  } else {
+    *octets_needed = 3;
+  }
+}
+
+/**
+ * Returns octet count for an u32_t.
+ *
+ * @param value value to be encoded
+ * @param octets_needed points to the return value
+ *
+ * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
+ * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
+ * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
+ */
+void
+snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed)
+{
+  if (value < 0x80UL) {
+    *octets_needed = 1;
+  } else if (value < 0x8000UL) {
+    *octets_needed = 2;
+  } else if (value < 0x800000UL) {
+    *octets_needed = 3;
+  } else if (value < 0x80000000UL) {
+    *octets_needed = 4;
+  } else {
+    *octets_needed = 5;
+  }
+}
+
+/**
+ * Returns octet count for an u64_t.
+ *
+ * @param value value to be encoded
+ * @param octets_needed points to the return value
+ *
+ * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
+ * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
+ * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
+ */
+void
+snmp_asn1_enc_u64t_cnt(const u32_t *value, u16_t *octets_needed)
+{
+  /* check if high u32 is 0 */
+  if (*value == 0x00) {
+    /* only low u32 is important */
+    value++;
+    snmp_asn1_enc_u32t_cnt(*value, octets_needed);
+  } else {
+    /* low u32 does not matter for length determination */
+    snmp_asn1_enc_u32t_cnt(*value, octets_needed);
+    *octets_needed = *octets_needed + 4; /* add the 4 bytes of low u32 */
+  }
+}
+
+/**
+ * Returns octet count for an s32_t.
+ *
+ * @param value value to be encoded
+ * @param octets_needed points to the return value
+ *
+ * @note ASN coded integers are _always_ signed.
+ */
+void
+snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed)
+{
+  if (value < 0) {
+    value = ~value;
+  }
+  if (value < 0x80L) {
+    *octets_needed = 1;
+  } else if (value < 0x8000L) {
+    *octets_needed = 2;
+  } else if (value < 0x800000L) {
+    *octets_needed = 3;
+  } else {
+    *octets_needed = 4;
+  }
+}
+
+/**
+ * Returns octet count for an object identifier.
+ *
+ * @param oid points to object identifier array
+ * @param oid_len object identifier array length
+ * @param octets_needed points to the return value
+ */
+void
+snmp_asn1_enc_oid_cnt(const u32_t *oid, u16_t oid_len, u16_t *octets_needed)
+{
+  u32_t sub_id;
+
+  *octets_needed = 0;
+  if (oid_len > 1) {
+    /* compressed prefix in one octet */
+    (*octets_needed)++;
+    oid_len -= 2;
+    oid += 2;
+  }
+  while (oid_len > 0) {
+    oid_len--;
+    sub_id = *oid;
+
+    sub_id >>= 7;
+    (*octets_needed)++;
+    while (sub_id > 0) {
+      sub_id >>= 7;
+      (*octets_needed)++;
+    }
+    oid++;
+  }
+}
+
+/**
+ * Decodes a TLV from a pbuf stream.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param tlv returns decoded TLV
+ * @return ERR_OK if successful, ERR_VAL if we can't decode
+ */
+err_t
+snmp_asn1_dec_tlv(struct snmp_pbuf_stream* pbuf_stream, struct snmp_asn1_tlv* tlv)
+{
+  u8_t data;
+
+  /* decode type first */
+  PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+  tlv->type = data;
+
+  if ((tlv->type & SNMP_ASN1_DATATYPE_MASK) == SNMP_ASN1_DATATYPE_EXTENDED) {
+    /* extended format is not used by SNMP so we do not accept those values */
+    return ERR_VAL;
+  }
+  tlv->type_len = 1;
+
+  /* now, decode length */
+  PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+
+  if (data < 0x80) { /* short form */
+    tlv->length_len = 1;
+    tlv->value_len  = data;
+  } else if (data > 0x80) { /* long form */
+    u8_t length_bytes = data - 0x80;
+    tlv->length_len = length_bytes + 1; /* this byte + defined number of length bytes following */
+    tlv->value_len = 0;
+
+    while (length_bytes > 0) {
+      /* we only support up to u16.maxvalue-1 (2 bytes) but have to accept leading zero bytes */
+      if (tlv->value_len > 0xFF) {
+        return ERR_VAL;
+      }
+      PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+      tlv->value_len <<= 8;
+      tlv->value_len |= data;
+
+      /* take care for special value used for indefinite length */
+      if (tlv->value_len == 0xFFFF) {
+        return ERR_VAL;
+      }
+
+      length_bytes--;
+    }
+  } else { /* data == 0x80 indefinite length form */
+    /* (not allowed for SNMP; RFC 1157, 3.2.2) */
+    return ERR_VAL;
+  }
+
+  return ERR_OK;
+}
+
+/**
+ * Decodes positive integer (counter, gauge, timeticks) into u32_t.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param len length of the coded integer field
+ * @param value return host order integer
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
+ *
+ * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
+ * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
+ * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
+ */
+err_t
+snmp_asn1_dec_u32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value)
+{
+  u8_t data;
+
+  if ((len > 0) && (len <= 5)) {
+    PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+
+    /* expecting sign bit to be zero, only unsigned please! */
+    if (((len == 5) && (data == 0x00)) || ((len < 5) && ((data & 0x80) == 0))) {
+      *value = data;
+      len--;
+
+      while (len > 0) {
+        PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+        len--;
+
+        *value <<= 8;
+        *value |= data;
+      }
+
+      return ERR_OK;
+    }
+  }
+
+  return ERR_VAL;
+}
+
+/**
+ * Decodes large positive integer (counter64) into 2x u32_t.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param len length of the coded integer field
+ * @param value return host order integer
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
+ *
+ * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
+ * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
+ * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
+ */
+err_t
+snmp_asn1_dec_u64t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value)
+{
+  u8_t data;
+
+  if (len <= 4) {
+    /* high u32 is 0 */
+    *value = 0;
+    /* directly skip to low u32 */
+    value++;
+  }
+
+  if ((len > 0) && (len <= 9)) {
+    PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+
+    /* expecting sign bit to be zero, only unsigned please! */
+    if (((len == 9) && (data == 0x00)) || ((len < 9) && ((data & 0x80) == 0))) {
+      *value = data;
+      len--;
+
+      while (len > 0) {
+        PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+
+        if (len == 4) {
+          /* skip to low u32 */
+          value++;
+          *value = 0;
+        } else {
+          *value <<= 8;
+        }
+
+        *value |= data;
+        len--;
+      }
+
+      return ERR_OK;
+    }
+  }
+
+  return ERR_VAL;
+}
+
+/**
+ * Decodes integer into s32_t.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param len length of the coded integer field
+ * @param value return host order integer
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
+ *
+ * @note ASN coded integers are _always_ signed!
+ */
+err_t
+snmp_asn1_dec_s32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, s32_t *value)
+{
+#if BYTE_ORDER == LITTLE_ENDIAN
+  u8_t *lsb_ptr = (u8_t*)value;
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+  u8_t *lsb_ptr = (u8_t*)value + sizeof(s32_t) - 1;
+#endif
+  u8_t sign;
+  u8_t data;
+
+  if ((len > 0) && (len < 5)) {
+    PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+    len--;
+
+    if (data & 0x80) {
+      /* negative, start from -1 */
+      *value = -1;
+      sign = 1;
+      *lsb_ptr &= data;
+    } else {
+      /* positive, start from 0 */
+      *value = 0;
+      sign = 0;
+      *lsb_ptr |= data;
+    }
+
+    /* OR/AND octets with value */
+    while (len > 0) {
+      PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+      len--;
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+      *value <<= 8;
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+      *value >>= 8;
+#endif
+
+      if (sign) {
+        *lsb_ptr |= 255;
+        *lsb_ptr &= data;
+      } else {
+        *lsb_ptr |= data;
+      }
+    }
+
+    return ERR_OK;
+  }
+
+  return ERR_VAL;
+}
+
+/**
+ * Decodes object identifier from incoming message into array of u32_t.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param len length of the coded object identifier
+ * @param oid return decoded object identifier
+ * @param oid_len return decoded object identifier length
+ * @param oid_max_len size of oid buffer
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
+ */
+err_t
+snmp_asn1_dec_oid(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t* oid, u8_t* oid_len, u8_t oid_max_len)
+{
+  u32_t *oid_ptr;
+  u8_t data;
+
+  *oid_len = 0;
+  oid_ptr = oid;
+  if (len > 0) {
+    if (oid_max_len < 2) {
+      return ERR_MEM;
+    }
+
+    PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+    len--;
+
+    /* first compressed octet */
+    if (data == 0x2B) {
+      /* (most) common case 1.3 (iso.org) */
+      *oid_ptr = 1;
+      oid_ptr++;
+      *oid_ptr = 3;
+      oid_ptr++;
+    } else if (data < 40) {
+      *oid_ptr = 0;
+      oid_ptr++;
+      *oid_ptr = data;
+      oid_ptr++;
+    } else if (data < 80) {
+      *oid_ptr = 1;
+      oid_ptr++;
+      *oid_ptr = data - 40;
+      oid_ptr++;
+    } else {
+      *oid_ptr = 2;
+      oid_ptr++;
+      *oid_ptr = data - 80;
+      oid_ptr++;
+    }
+    *oid_len = 2;
+  } else {
+    /* accepting zero length identifiers e.g. for getnext operation. uncommon but valid */
+    return ERR_OK;
+  }
+
+  while ((len > 0) && (*oid_len < oid_max_len)) {
+    PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+    len--;
+
+    if ((data & 0x80) == 0x00) {
+      /* sub-identifier uses single octet */
+      *oid_ptr = data;
+    } else {
+      /* sub-identifier uses multiple octets */
+      u32_t sub_id = (data & ~0x80);
+      while ((len > 0) && ((data & 0x80) != 0)) {
+        PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+        len--;
+
+        sub_id = (sub_id << 7) + (data & ~0x80);
+      }
+
+      if ((data & 0x80) != 0) {
+        /* "more bytes following" bit still set at end of len */
+        return ERR_VAL;
+      }
+      *oid_ptr = sub_id;
+    }
+    oid_ptr++;
+    (*oid_len)++;
+  }
+
+  if (len > 0) {
+    /* OID to long to fit in our buffer */
+    return ERR_MEM;
+  }
+
+  return ERR_OK;
+}
+
+/**
+ * Decodes (copies) raw data (ip-addresses, octet strings, opaque encoding)
+ * from incoming message into array.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param len length of the coded raw data (zero is valid, e.g. empty string!)
+ * @param buf return raw bytes
+ * @param buf_len returns length of the raw return value
+ * @param buf_max_len buffer size
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
+ */
+err_t
+snmp_asn1_dec_raw(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u8_t *buf, u16_t* buf_len, u16_t buf_max_len)
+{
+  if (len > buf_max_len) {
+    /* not enough dst space */
+    return ERR_MEM;
+  }
+  *buf_len = len;
+
+  while (len > 0) {
+    PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, buf));
+    buf++;
+    len--;
+  }
+
+  return ERR_OK;
+}
+
+#endif /* LWIP_SNMP */

+ 108 - 113
libs/thirdparty/lwip_2.1.2/src/apps/snmp/snmp_asn1.h → libs/thirdparty/LwIP/src/apps/snmp/snmp_asn1.h

@@ -1,113 +1,108 @@
-/**
- * @file
- * Abstract Syntax Notation One (ISO 8824, 8825) codec.
- */
-
-/*
- * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
- * Copyright (c) 2016 Elias Oenal.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * Author: Christiaan Simons <christiaan.simons@axon.tv>
- *         Martin Hentschel <info@cl-soft.de>
- *         Elias Oenal <lwip@eliasoenal.com>
- */
-
-#ifndef LWIP_HDR_APPS_SNMP_ASN1_H
-#define LWIP_HDR_APPS_SNMP_ASN1_H
-
-#include "lwip/apps/snmp_opts.h"
-
-#if LWIP_SNMP
-
-#include "lwip/err.h"
-#include "lwip/apps/snmp_core.h"
-#include "snmp_pbuf_stream.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define SNMP_ASN1_TLV_INDEFINITE_LENGTH 0x80
-
-#define SNMP_ASN1_CLASS_MASK        0xC0
-#define SNMP_ASN1_CONTENTTYPE_MASK  0x20
-#define SNMP_ASN1_DATATYPE_MASK     0x1F
-#define SNMP_ASN1_DATATYPE_EXTENDED 0x1F /* DataType indicating that datatype is encoded in following bytes */
-
-/* context specific (SNMP) tags (from SNMP spec. RFC1157 and RFC1905) */
-#define SNMP_ASN1_CONTEXT_PDU_GET_REQ      0
-#define SNMP_ASN1_CONTEXT_PDU_GET_NEXT_REQ 1
-#define SNMP_ASN1_CONTEXT_PDU_GET_RESP     2
-#define SNMP_ASN1_CONTEXT_PDU_SET_REQ      3
-#define SNMP_ASN1_CONTEXT_PDU_TRAP         4
-#define SNMP_ASN1_CONTEXT_PDU_GET_BULK_REQ 5
-#define SNMP_ASN1_CONTEXT_PDU_INFORM_REQ   6
-#define SNMP_ASN1_CONTEXT_PDU_V2_TRAP      7
-#define SNMP_ASN1_CONTEXT_PDU_REPORT       8
-
-#define SNMP_ASN1_CONTEXT_VARBIND_NO_SUCH_OBJECT      0
-#define SNMP_ASN1_CONTEXT_VARBIND_END_OF_MIB_VIEW     2
-
-struct snmp_asn1_tlv {
-  u8_t  type;       /* only U8 because extended types are not specified by SNMP */
-  u8_t  type_len;   /* encoded length of 'type' field (normally 1) */
-  u8_t  length_len; /* indicates how many bytes are required to encode the 'value_len' field */
-  u16_t value_len;  /* encoded length of the value */
-};
-#define SNMP_ASN1_TLV_HDR_LENGTH(tlv) ((tlv).type_len + (tlv).length_len)
-#define SNMP_ASN1_TLV_LENGTH(tlv) ((tlv).type_len + (tlv).length_len + (tlv).value_len)
-#define SNMP_ASN1_SET_TLV_PARAMS(tlv, type_, length_len_, value_len_) do { (tlv).type = (type_); (tlv).type_len = 0; (tlv).length_len = (length_len_); (tlv).value_len = (value_len_); } while (0);
-
-err_t snmp_asn1_dec_tlv(struct snmp_pbuf_stream *pbuf_stream, struct snmp_asn1_tlv *tlv);
-err_t snmp_asn1_dec_u32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value);
-err_t snmp_asn1_dec_s32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, s32_t *value);
-err_t snmp_asn1_dec_oid(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *oid, u8_t *oid_len, u8_t oid_max_len);
-err_t snmp_asn1_dec_raw(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u8_t *buf, u16_t *buf_len, u16_t buf_max_len);
-
-err_t snmp_ans1_enc_tlv(struct snmp_pbuf_stream *pbuf_stream, struct snmp_asn1_tlv *tlv);
-
-void snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed);
-void snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed);
-void snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed);
-void snmp_asn1_enc_oid_cnt(const u32_t *oid, u16_t oid_len, u16_t *octets_needed);
-err_t snmp_asn1_enc_oid(struct snmp_pbuf_stream *pbuf_stream, const u32_t *oid, u16_t oid_len);
-err_t snmp_asn1_enc_s32t(struct snmp_pbuf_stream *pbuf_stream, u16_t octets_needed, s32_t value);
-err_t snmp_asn1_enc_u32t(struct snmp_pbuf_stream *pbuf_stream, u16_t octets_needed, u32_t value);
-err_t snmp_asn1_enc_raw(struct snmp_pbuf_stream *pbuf_stream, const u8_t *raw, u16_t raw_len);
-
-#if LWIP_HAVE_INT64
-err_t snmp_asn1_dec_u64t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u64_t *value);
-void snmp_asn1_enc_u64t_cnt(u64_t value, u16_t *octets_needed);
-err_t snmp_asn1_enc_u64t(struct snmp_pbuf_stream *pbuf_stream, u16_t octets_needed, u64_t value);
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* LWIP_SNMP */
-
-#endif /* LWIP_HDR_APPS_SNMP_ASN1_H */
+/**
+ * @file
+ * Abstract Syntax Notation One (ISO 8824, 8825) codec.
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * Copyright (c) 2016 Elias Oenal.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Christiaan Simons <christiaan.simons@axon.tv>
+ *         Martin Hentschel <info@cl-soft.de>
+ *         Elias Oenal <lwip@eliasoenal.com>
+ */
+
+#ifndef LWIP_HDR_APPS_SNMP_ASN1_H
+#define LWIP_HDR_APPS_SNMP_ASN1_H
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP
+
+#include "lwip/err.h"
+#include "lwip/apps/snmp_core.h"
+#include "snmp_pbuf_stream.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SNMP_ASN1_TLV_INDEFINITE_LENGTH 0x80
+
+#define SNMP_ASN1_CLASS_MASK        0xC0
+#define SNMP_ASN1_CONTENTTYPE_MASK  0x20
+#define SNMP_ASN1_DATATYPE_MASK     0x1F
+#define SNMP_ASN1_DATATYPE_EXTENDED 0x1F /* DataType indicating that datatype is encoded in following bytes */
+
+/* context specific (SNMP) tags (from SNMP spec. RFC1157) */
+#define SNMP_ASN1_CONTEXT_PDU_GET_REQ      0
+#define SNMP_ASN1_CONTEXT_PDU_GET_NEXT_REQ 1
+#define SNMP_ASN1_CONTEXT_PDU_GET_RESP     2
+#define SNMP_ASN1_CONTEXT_PDU_SET_REQ      3
+#define SNMP_ASN1_CONTEXT_PDU_TRAP         4
+#define SNMP_ASN1_CONTEXT_PDU_GET_BULK_REQ 5
+
+#define SNMP_ASN1_CONTEXT_VARBIND_NO_SUCH_OBJECT      0
+#define SNMP_ASN1_CONTEXT_VARBIND_END_OF_MIB_VIEW     2
+
+struct snmp_asn1_tlv
+{
+  u8_t  type;       /* only U8 because extended types are not specified by SNMP */
+  u8_t  type_len;   /* encoded length of 'type' field (normally 1) */
+  u8_t  length_len; /* indicates how many bytes are required to encode the 'value_len' field */
+  u16_t value_len;  /* encoded length of the value */
+};
+#define SNMP_ASN1_TLV_HDR_LENGTH(tlv) ((tlv).type_len + (tlv).length_len)
+#define SNMP_ASN1_TLV_LENGTH(tlv) ((tlv).type_len + (tlv).length_len + (tlv).value_len)
+#define SNMP_ASN1_SET_TLV_PARAMS(tlv, type_, length_len_, value_len_) do { (tlv).type = (type_); (tlv).type_len = 0; (tlv).length_len = (length_len_); (tlv).value_len = (value_len_); } while (0);
+
+err_t snmp_asn1_dec_tlv(struct snmp_pbuf_stream* pbuf_stream, struct snmp_asn1_tlv* tlv);
+err_t snmp_asn1_dec_u32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value);
+err_t snmp_asn1_dec_u64t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value);
+err_t snmp_asn1_dec_s32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, s32_t *value);
+err_t snmp_asn1_dec_oid(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t* oid, u8_t* oid_len, u8_t oid_max_len);
+err_t snmp_asn1_dec_raw(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u8_t *buf, u16_t* buf_len, u16_t buf_max_len);
+
+err_t snmp_ans1_enc_tlv(struct snmp_pbuf_stream* pbuf_stream, struct snmp_asn1_tlv* tlv);
+
+void snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed);
+void snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed);
+void snmp_asn1_enc_u64t_cnt(const u32_t *value, u16_t *octets_needed);
+void snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed);
+void snmp_asn1_enc_oid_cnt(const u32_t *oid, u16_t oid_len, u16_t *octets_needed);
+err_t snmp_asn1_enc_oid(struct snmp_pbuf_stream* pbuf_stream, const u32_t *oid, u16_t oid_len);
+err_t snmp_asn1_enc_s32t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, s32_t value);
+err_t snmp_asn1_enc_u32t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, u32_t value);
+err_t snmp_asn1_enc_u64t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, const u32_t* value);
+err_t snmp_asn1_enc_raw(struct snmp_pbuf_stream* pbuf_stream, const u8_t *raw, u16_t raw_len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_SNMP */
+
+#endif /* LWIP_HDR_APPS_SNMP_ASN1_H */

+ 1349 - 1353
libs/thirdparty/lwip_2.1.2/src/apps/snmp/snmp_core.c → libs/thirdparty/LwIP/src/apps/snmp/snmp_core.c

@@ -1,1353 +1,1349 @@
-/**
- * @file
- * MIB tree access/construction functions.
- */
-
-/*
- * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * Author: Christiaan Simons <christiaan.simons@axon.tv>
- *         Martin Hentschel <info@cl-soft.de>
-*/
-
-/**
- * @defgroup snmp SNMPv2c/v3 agent
- * @ingroup apps
- * SNMPv2c and SNMPv3 compatible agent\n
- * There is also a MIB compiler and a MIB viewer in lwIP contrib repository
- * (lwip-contrib/apps/LwipMibCompiler).\n
- * The agent implements the most important MIB2 MIBs including IPv6 support
- * (interfaces, UDP, TCP, SNMP, ICMP, SYSTEM). IP MIB is an older version
- * without IPv6 statistics (TODO).\n
- * Rewritten by Martin Hentschel <info@cl-soft.de> and
- * Dirk Ziegelmeier <dziegel@gmx.de>\n
- *
- * 0 Agent Capabilities
- * ====================
- *
- * Features:
- * ---------
- * - SNMPv2c support.
- * - SNMPv3 support (a port to ARM mbedtls is provided, LWIP_SNMP_V3_MBEDTLS option).
- * - Low RAM usage - no memory pools, stack only.
- * - MIB2 implementation is separated from SNMP stack.
- * - Support for multiple MIBs (snmp_set_mibs() call) - e.g. for private MIB.
- * - Simple and generic API for MIB implementation.
- * - Comfortable node types and helper functions for scalar arrays and tables.
- * - Counter64, bit and truthvalue datatype support.
- * - Callbacks for SNMP writes e.g. to implement persistency.
- * - Runs on two APIs: RAW and netconn.
- * - Async API is gone - the stack now supports netconn API instead,
- *   so blocking operations can be done in MIB calls.
- *   SNMP runs in a worker thread when netconn API is used.
- * - Simplified thread sync support for MIBs - useful when MIBs
- *   need to access variables shared with other threads where no locking is
- *   possible. Used in MIB2 to access lwIP stats from lwIP thread.
- *
- * MIB compiler (code generator):
- * ------------------------------
- * - Provided in lwIP contrib repository.
- * - Written in C#. MIB viewer used Windows Forms.
- * - Developed on Windows with Visual Studio 2010.
- * - Can be compiled and used on all platforms with http://www.monodevelop.com/.
- * - Based on a heavily modified version of of SharpSnmpLib (a4bd05c6afb4)
- *   (https://sharpsnmplib.codeplex.com/SourceControl/network/forks/Nemo157/MIBParserUpdate).
- * - MIB parser, C file generation framework and LWIP code generation are cleanly
- *   separated, which means the code may be useful as a base for code generation
- *   of other SNMP agents.
- *
- * Notes:
- * ------
- * - Stack and MIB compiler were used to implement a Profinet device.
- *   Compiled/implemented MIBs: LLDP-MIB, LLDP-EXT-DOT3-MIB, LLDP-EXT-PNO-MIB.
- *
- * SNMPv1 per RFC1157 and SNMPv2c per RFC 3416
- * -------------------------------------------
- *   Note the S in SNMP stands for "Simple". Note that "Simple" is
- *   relative. SNMP is simple compared to the complex ISO network
- *   management protocols CMIP (Common Management Information Protocol)
- *   and CMOT (CMip Over Tcp).
- *
- * SNMPv3
- * ------
- * When SNMPv3 is used, several functions from snmpv3.h must be implemented
- * by the user. This is mainly user management and persistence handling.
- * The sample provided in lwip-contrib is insecure, don't use it in production
- * systems, especially the missing persistence for engine boots variable
- * simplifies replay attacks.
- *
- * MIB II
- * ------
- *   The standard lwIP stack management information base.
- *   This is a required MIB, so this is always enabled.
- *   The groups EGP, CMOT and transmission are disabled by default.
- *
- *   Most mib-2 objects are not writable except:
- *   sysName, sysLocation, sysContact, snmpEnableAuthenTraps.
- *   Writing to or changing the ARP and IP address and route
- *   tables is not possible.
- *
- *   Note lwIP has a very limited notion of IP routing. It currently
- *   doen't have a route table and doesn't have a notion of the U,G,H flags.
- *   Instead lwIP uses the interface list with only one default interface
- *   acting as a single gateway interface (G) for the default route.
- *
- *   The agent returns a "virtual table" with the default route 0.0.0.0
- *   for the default interface and network routes (no H) for each
- *   network interface in the netif_list.
- *   All routes are considered to be up (U).
- *
- * Loading additional MIBs
- * -----------------------
- *   MIBs can only be added in compile-time, not in run-time.
- *
- *
- * 1 Building the Agent
- * ====================
- * First of all you'll need to add the following define
- * to your local lwipopts.h:
- * \#define LWIP_SNMP               1
- *
- * and add the source files your makefile.
- *
- * Note you'll might need to adapt you network driver to update
- * the mib2 variables for your interface.
- *
- * 2 Running the Agent
- * ===================
- * The following function calls must be made in your program to
- * actually get the SNMP agent running.
- *
- * Before starting the agent you should supply pointers
- * for sysContact, sysLocation, and snmpEnableAuthenTraps.
- * You can do this by calling
- *
- * - snmp_mib2_set_syscontact()
- * - snmp_mib2_set_syslocation()
- * - snmp_set_auth_traps_enabled()
- *
- * You can register a callback which is called on successful write access:
- * snmp_set_write_callback().
- *
- * Additionally you may want to set
- *
- * - snmp_mib2_set_sysdescr()
- * - snmp_set_device_enterprise_oid()
- * - snmp_mib2_set_sysname()
- *
- * Also before starting the agent you need to setup
- * one or more trap destinations using these calls:
- *
- * - snmp_trap_dst_enable()
- * - snmp_trap_dst_ip_set()
- *
- * If you need more than MIB2, set the MIBs you want to use
- * by snmp_set_mibs().
- *
- * Finally, enable the agent by calling snmp_init()
- *
- * @defgroup snmp_core Core
- * @ingroup snmp
- *
- * @defgroup snmp_traps Traps
- * @ingroup snmp
- */
-
-#include "lwip/apps/snmp_opts.h"
-
-#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
-
-#include "lwip/apps/snmp.h"
-#include "lwip/apps/snmp_core.h"
-#include "snmp_core_priv.h"
-#include "lwip/netif.h"
-#include <string.h>
-
-
-#if (LWIP_SNMP && (SNMP_TRAP_DESTINATIONS<=0))
-#error "If you want to use SNMP, you have to define SNMP_TRAP_DESTINATIONS>=1 in your lwipopts.h"
-#endif
-#if (!LWIP_UDP && LWIP_SNMP)
-#error "If you want to use SNMP, you have to define LWIP_UDP=1 in your lwipopts.h"
-#endif
-#if SNMP_MAX_OBJ_ID_LEN > 255
-#error "SNMP_MAX_OBJ_ID_LEN must fit into an u8_t"
-#endif
-
-struct snmp_statistics snmp_stats;
-static const struct snmp_obj_id  snmp_device_enterprise_oid_default = {SNMP_DEVICE_ENTERPRISE_OID_LEN, SNMP_DEVICE_ENTERPRISE_OID};
-static const struct snmp_obj_id *snmp_device_enterprise_oid         = &snmp_device_enterprise_oid_default;
-
-const u32_t snmp_zero_dot_zero_values[] = { 0, 0 };
-const struct snmp_obj_id_const_ref snmp_zero_dot_zero = { LWIP_ARRAYSIZE(snmp_zero_dot_zero_values), snmp_zero_dot_zero_values };
-
-#if SNMP_LWIP_MIB2 && LWIP_SNMP_V3
-#include "lwip/apps/snmp_mib2.h"
-#include "lwip/apps/snmp_snmpv2_framework.h"
-#include "lwip/apps/snmp_snmpv2_usm.h"
-static const struct snmp_mib *const default_mibs[] = { &mib2, &snmpframeworkmib, &snmpusmmib };
-static u8_t snmp_num_mibs                          = LWIP_ARRAYSIZE(default_mibs);
-#elif SNMP_LWIP_MIB2
-#include "lwip/apps/snmp_mib2.h"
-static const struct snmp_mib *const default_mibs[] = { &mib2 };
-static u8_t snmp_num_mibs                          = LWIP_ARRAYSIZE(default_mibs);
-#else
-static const struct snmp_mib *const default_mibs[] = { NULL };
-static u8_t snmp_num_mibs                          = 0;
-#endif
-
-/* List of known mibs */
-static struct snmp_mib const *const *snmp_mibs = default_mibs;
-
-/**
- * @ingroup snmp_core
- * Sets the MIBs to use.
- * Example: call snmp_set_mibs() as follows:
- * static const struct snmp_mib *my_snmp_mibs[] = {
- *   &mib2,
- *   &private_mib
- * };
- * snmp_set_mibs(my_snmp_mibs, LWIP_ARRAYSIZE(my_snmp_mibs));
- */
-void
-snmp_set_mibs(const struct snmp_mib **mibs, u8_t num_mibs)
-{
-  LWIP_ASSERT_CORE_LOCKED();
-  LWIP_ASSERT("mibs pointer must be != NULL", (mibs != NULL));
-  LWIP_ASSERT("num_mibs pointer must be != 0", (num_mibs != 0));
-  snmp_mibs     = mibs;
-  snmp_num_mibs = num_mibs;
-}
-
-/**
- * @ingroup snmp_core
- * 'device enterprise oid' is used for 'device OID' field in trap PDU's (for identification of generating device)
- * as well as for value returned by MIB-2 'sysObjectID' field (if internal MIB2 implementation is used).
- * The 'device enterprise oid' shall point to an OID located under 'private-enterprises' branch (1.3.6.1.4.1.XXX). If a vendor
- * wants to provide a custom object there, he has to get its own enterprise oid from IANA (http://www.iana.org). It
- * is not allowed to use LWIP enterprise ID!
- * In order to identify a specific device it is recommended to create a dedicated OID for each device type under its own
- * enterprise oid.
- * e.g.
- * device a > 1.3.6.1.4.1.XXX(ent-oid).1(devices).1(device a)
- * device b > 1.3.6.1.4.1.XXX(ent-oid).1(devices).2(device b)
- * for more details see description of 'sysObjectID' field in RFC1213-MIB
- */
-void snmp_set_device_enterprise_oid(const struct snmp_obj_id *device_enterprise_oid)
-{
-  LWIP_ASSERT_CORE_LOCKED();
-  if (device_enterprise_oid == NULL) {
-    snmp_device_enterprise_oid = &snmp_device_enterprise_oid_default;
-  } else {
-    snmp_device_enterprise_oid = device_enterprise_oid;
-  }
-}
-
-/**
- * @ingroup snmp_core
- * Get 'device enterprise oid'
- */
-const struct snmp_obj_id *snmp_get_device_enterprise_oid(void)
-{
-  LWIP_ASSERT_CORE_LOCKED();
-  return snmp_device_enterprise_oid;
-}
-
-#if LWIP_IPV4
-/**
- * Conversion from InetAddressIPv4 oid to lwIP ip4_addr
- * @param oid points to u32_t ident[4] input
- * @param ip points to output struct
- */
-u8_t
-snmp_oid_to_ip4(const u32_t *oid, ip4_addr_t *ip)
-{
-  if ((oid[0] > 0xFF) ||
-      (oid[1] > 0xFF) ||
-      (oid[2] > 0xFF) ||
-      (oid[3] > 0xFF)) {
-    ip4_addr_copy(*ip, *IP4_ADDR_ANY4);
-    return 0;
-  }
-
-  IP4_ADDR(ip, oid[0], oid[1], oid[2], oid[3]);
-  return 1;
-}
-
-/**
- * Convert ip4_addr to InetAddressIPv4 (no InetAddressType)
- * @param ip points to input struct
- * @param oid points to u32_t ident[4] output
- */
-void
-snmp_ip4_to_oid(const ip4_addr_t *ip, u32_t *oid)
-{
-  oid[0] = ip4_addr1(ip);
-  oid[1] = ip4_addr2(ip);
-  oid[2] = ip4_addr3(ip);
-  oid[3] = ip4_addr4(ip);
-}
-#endif /* LWIP_IPV4 */
-
-#if LWIP_IPV6
-/**
- * Conversion from InetAddressIPv6 oid to lwIP ip6_addr
- * @param oid points to u32_t oid[16] input
- * @param ip points to output struct
- */
-u8_t
-snmp_oid_to_ip6(const u32_t *oid, ip6_addr_t *ip)
-{
-  if ((oid[0]  > 0xFF) ||
-      (oid[1]  > 0xFF) ||
-      (oid[2]  > 0xFF) ||
-      (oid[3]  > 0xFF) ||
-      (oid[4]  > 0xFF) ||
-      (oid[5]  > 0xFF) ||
-      (oid[6]  > 0xFF) ||
-      (oid[7]  > 0xFF) ||
-      (oid[8]  > 0xFF) ||
-      (oid[9]  > 0xFF) ||
-      (oid[10] > 0xFF) ||
-      (oid[11] > 0xFF) ||
-      (oid[12] > 0xFF) ||
-      (oid[13] > 0xFF) ||
-      (oid[14] > 0xFF) ||
-      (oid[15] > 0xFF)) {
-    ip6_addr_set_any(ip);
-    return 0;
-  }
-
-  ip->addr[0] = (oid[0]  << 24) | (oid[1]  << 16) | (oid[2]  << 8) | (oid[3]  << 0);
-  ip->addr[1] = (oid[4]  << 24) | (oid[5]  << 16) | (oid[6]  << 8) | (oid[7]  << 0);
-  ip->addr[2] = (oid[8]  << 24) | (oid[9]  << 16) | (oid[10] << 8) | (oid[11] << 0);
-  ip->addr[3] = (oid[12] << 24) | (oid[13] << 16) | (oid[14] << 8) | (oid[15] << 0);
-  return 1;
-}
-
-/**
- * Convert ip6_addr to InetAddressIPv6 (no InetAddressType)
- * @param ip points to input struct
- * @param oid points to u32_t ident[16] output
- */
-void
-snmp_ip6_to_oid(const ip6_addr_t *ip, u32_t *oid)
-{
-  oid[0]  = (ip->addr[0] & 0xFF000000) >> 24;
-  oid[1]  = (ip->addr[0] & 0x00FF0000) >> 16;
-  oid[2]  = (ip->addr[0] & 0x0000FF00) >>  8;
-  oid[3]  = (ip->addr[0] & 0x000000FF) >>  0;
-  oid[4]  = (ip->addr[1] & 0xFF000000) >> 24;
-  oid[5]  = (ip->addr[1] & 0x00FF0000) >> 16;
-  oid[6]  = (ip->addr[1] & 0x0000FF00) >>  8;
-  oid[7]  = (ip->addr[1] & 0x000000FF) >>  0;
-  oid[8]  = (ip->addr[2] & 0xFF000000) >> 24;
-  oid[9]  = (ip->addr[2] & 0x00FF0000) >> 16;
-  oid[10] = (ip->addr[2] & 0x0000FF00) >>  8;
-  oid[11] = (ip->addr[2] & 0x000000FF) >>  0;
-  oid[12] = (ip->addr[3] & 0xFF000000) >> 24;
-  oid[13] = (ip->addr[3] & 0x00FF0000) >> 16;
-  oid[14] = (ip->addr[3] & 0x0000FF00) >>  8;
-  oid[15] = (ip->addr[3] & 0x000000FF) >>  0;
-}
-#endif /* LWIP_IPV6 */
-
-#if LWIP_IPV4 || LWIP_IPV6
-/**
- * Convert to InetAddressType+InetAddress+InetPortNumber
- * @param ip IP address
- * @param port Port
- * @param oid OID
- * @return OID length
- */
-u8_t
-snmp_ip_port_to_oid(const ip_addr_t *ip, u16_t port, u32_t *oid)
-{
-  u8_t idx;
-
-  idx = snmp_ip_to_oid(ip, oid);
-  oid[idx] = port;
-  idx++;
-
-  return idx;
-}
-
-/**
- * Convert to InetAddressType+InetAddress
- * @param ip IP address
- * @param oid OID
- * @return OID length
- */
-u8_t
-snmp_ip_to_oid(const ip_addr_t *ip, u32_t *oid)
-{
-  if (IP_IS_ANY_TYPE_VAL(*ip)) {
-    oid[0] = 0; /* any */
-    oid[1] = 0; /* no IP OIDs follow */
-    return 2;
-  } else if (IP_IS_V6(ip)) {
-#if LWIP_IPV6
-    oid[0] = 2; /* ipv6 */
-    oid[1] = 16; /* 16 InetAddressIPv6 OIDs follow */
-    snmp_ip6_to_oid(ip_2_ip6(ip), &oid[2]);
-    return 18;
-#else /* LWIP_IPV6 */
-    return 0;
-#endif /* LWIP_IPV6 */
-  } else {
-#if LWIP_IPV4
-    oid[0] = 1; /* ipv4 */
-    oid[1] = 4; /* 4 InetAddressIPv4 OIDs follow */
-    snmp_ip4_to_oid(ip_2_ip4(ip), &oid[2]);
-    return 6;
-#else /* LWIP_IPV4 */
-    return 0;
-#endif /* LWIP_IPV4 */
-  }
-}
-
-/**
- * Convert from InetAddressType+InetAddress to ip_addr_t
- * @param oid OID
- * @param oid_len OID length
- * @param ip IP address
- * @return Parsed OID length
- */
-u8_t
-snmp_oid_to_ip(const u32_t *oid, u8_t oid_len, ip_addr_t *ip)
-{
-  /* InetAddressType */
-  if (oid_len < 1) {
-    return 0;
-  }
-
-  if (oid[0] == 0) { /* any */
-    /* 1x InetAddressType, 1x OID len */
-    if (oid_len < 2) {
-      return 0;
-    }
-    if (oid[1] != 0) {
-      return 0;
-    }
-
-    memset(ip, 0, sizeof(*ip));
-    IP_SET_TYPE(ip, IPADDR_TYPE_ANY);
-
-    return 2;
-  } else if (oid[0] == 1) { /* ipv4 */
-#if LWIP_IPV4
-    /* 1x InetAddressType, 1x OID len, 4x InetAddressIPv4 */
-    if (oid_len < 6) {
-      return 0;
-    }
-
-    /* 4x ipv4 OID */
-    if (oid[1] != 4) {
-      return 0;
-    }
-
-    IP_SET_TYPE(ip, IPADDR_TYPE_V4);
-    if (!snmp_oid_to_ip4(&oid[2], ip_2_ip4(ip))) {
-      return 0;
-    }
-
-    return 6;
-#else /* LWIP_IPV4 */
-    return 0;
-#endif /* LWIP_IPV4 */
-  } else if (oid[0] == 2) { /* ipv6 */
-#if LWIP_IPV6
-    /* 1x InetAddressType, 1x OID len, 16x InetAddressIPv6 */
-    if (oid_len < 18) {
-      return 0;
-    }
-
-    /* 16x ipv6 OID */
-    if (oid[1] != 16) {
-      return 0;
-    }
-
-    IP_SET_TYPE(ip, IPADDR_TYPE_V6);
-    if (!snmp_oid_to_ip6(&oid[2], ip_2_ip6(ip))) {
-      return 0;
-    }
-
-    return 18;
-#else /* LWIP_IPV6 */
-    return 0;
-#endif /* LWIP_IPV6 */
-  } else { /* unsupported InetAddressType */
-    return 0;
-  }
-}
-
-/**
- * Convert from InetAddressType+InetAddress+InetPortNumber to ip_addr_t and u16_t
- * @param oid OID
- * @param oid_len OID length
- * @param ip IP address
- * @param port Port
- * @return Parsed OID length
- */
-u8_t
-snmp_oid_to_ip_port(const u32_t *oid, u8_t oid_len, ip_addr_t *ip, u16_t *port)
-{
-  u8_t idx;
-
-  /* InetAddressType + InetAddress */
-  idx = snmp_oid_to_ip(&oid[0], oid_len, ip);
-  if (idx == 0) {
-    return 0;
-  }
-
-  /* InetPortNumber */
-  if (oid_len < (idx + 1)) {
-    return 0;
-  }
-  if (oid[idx] > 0xffff) {
-    return 0;
-  }
-  *port = (u16_t)oid[idx];
-  idx++;
-
-  return idx;
-}
-
-#endif /* LWIP_IPV4 || LWIP_IPV6 */
-
-/**
- * Assign an OID to struct snmp_obj_id
- * @param target Assignment target
- * @param oid OID
- * @param oid_len OID length
- */
-void
-snmp_oid_assign(struct snmp_obj_id *target, const u32_t *oid, u8_t oid_len)
-{
-  LWIP_ASSERT("oid_len <= SNMP_MAX_OBJ_ID_LEN", oid_len <= SNMP_MAX_OBJ_ID_LEN);
-
-  target->len = oid_len;
-
-  if (oid_len > 0) {
-    MEMCPY(target->id, oid, oid_len * sizeof(u32_t));
-  }
-}
-
-/**
- * Prefix an OID to OID in struct snmp_obj_id
- * @param target Assignment target to prefix
- * @param oid OID
- * @param oid_len OID length
- */
-void
-snmp_oid_prefix(struct snmp_obj_id *target, const u32_t *oid, u8_t oid_len)
-{
-  LWIP_ASSERT("target->len + oid_len <= SNMP_MAX_OBJ_ID_LEN", (target->len + oid_len) <= SNMP_MAX_OBJ_ID_LEN);
-
-  if (oid_len > 0) {
-    /* move existing OID to make room at the beginning for OID to insert */
-    int i;
-    for (i = target->len - 1; i >= 0; i--) {
-      target->id[i + oid_len] = target->id[i];
-    }
-
-    /* paste oid at the beginning */
-    MEMCPY(target->id, oid, oid_len * sizeof(u32_t));
-  }
-}
-
-/**
- * Combine two OIDs into struct snmp_obj_id
- * @param target Assignmet target
- * @param oid1 OID 1
- * @param oid1_len OID 1 length
- * @param oid2 OID 2
- * @param oid2_len OID 2 length
- */
-void
-snmp_oid_combine(struct snmp_obj_id *target, const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len)
-{
-  snmp_oid_assign(target, oid1, oid1_len);
-  snmp_oid_append(target, oid2, oid2_len);
-}
-
-/**
- * Append OIDs to struct snmp_obj_id
- * @param target Assignment target to append to
- * @param oid OID
- * @param oid_len OID length
- */
-void
-snmp_oid_append(struct snmp_obj_id *target, const u32_t *oid, u8_t oid_len)
-{
-  LWIP_ASSERT("offset + oid_len <= SNMP_MAX_OBJ_ID_LEN", (target->len + oid_len) <= SNMP_MAX_OBJ_ID_LEN);
-
-  if (oid_len > 0) {
-    MEMCPY(&target->id[target->len], oid, oid_len * sizeof(u32_t));
-    target->len = (u8_t)(target->len + oid_len);
-  }
-}
-
-/**
- * Compare two OIDs
- * @param oid1 OID 1
- * @param oid1_len OID 1 length
- * @param oid2 OID 2
- * @param oid2_len OID 2 length
- * @return -1: OID1&lt;OID2  1: OID1 &gt;OID2 0: equal
- */
-s8_t
-snmp_oid_compare(const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len)
-{
-  u8_t level = 0;
-  LWIP_ASSERT("'oid1' param must not be NULL or 'oid1_len' param be 0!", (oid1 != NULL) || (oid1_len == 0));
-  LWIP_ASSERT("'oid2' param must not be NULL or 'oid2_len' param be 0!", (oid2 != NULL) || (oid2_len == 0));
-
-  while ((level < oid1_len) && (level < oid2_len)) {
-    if (*oid1 < *oid2) {
-      return -1;
-    }
-    if (*oid1 > *oid2) {
-      return 1;
-    }
-
-    level++;
-    oid1++;
-    oid2++;
-  }
-
-  /* common part of both OID's is equal, compare length */
-  if (oid1_len < oid2_len) {
-    return -1;
-  }
-  if (oid1_len > oid2_len) {
-    return 1;
-  }
-
-  /* they are equal */
-  return 0;
-}
-
-
-/**
- * Check of two OIDs are equal
- * @param oid1 OID 1
- * @param oid1_len OID 1 length
- * @param oid2 OID 2
- * @param oid2_len OID 2 length
- * @return 1: equal 0: non-equal
- */
-u8_t
-snmp_oid_equal(const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len)
-{
-  return (snmp_oid_compare(oid1, oid1_len, oid2, oid2_len) == 0) ? 1 : 0;
-}
-
-/**
- * Convert netif to interface index
- * @param netif netif
- * @return index
- */
-u8_t
-netif_to_num(const struct netif *netif)
-{
-  return netif_get_index(netif);
-}
-
-static const struct snmp_mib *
-snmp_get_mib_from_oid(const u32_t *oid, u8_t oid_len)
-{
-  const u32_t *list_oid;
-  const u32_t *searched_oid;
-  u8_t i, l;
-
-  u8_t max_match_len = 0;
-  const struct snmp_mib *matched_mib = NULL;
-
-  LWIP_ASSERT("'oid' param must not be NULL!", (oid != NULL));
-
-  if (oid_len == 0) {
-    return NULL;
-  }
-
-  for (i = 0; i < snmp_num_mibs; i++) {
-    LWIP_ASSERT("MIB array not initialized correctly", (snmp_mibs[i] != NULL));
-    LWIP_ASSERT("MIB array not initialized correctly - base OID is NULL", (snmp_mibs[i]->base_oid != NULL));
-
-    if (oid_len >= snmp_mibs[i]->base_oid_len) {
-      l            = snmp_mibs[i]->base_oid_len;
-      list_oid     = snmp_mibs[i]->base_oid;
-      searched_oid = oid;
-
-      while (l > 0) {
-        if (*list_oid != *searched_oid) {
-          break;
-        }
-
-        l--;
-        list_oid++;
-        searched_oid++;
-      }
-
-      if ((l == 0) && (snmp_mibs[i]->base_oid_len > max_match_len)) {
-        max_match_len = snmp_mibs[i]->base_oid_len;
-        matched_mib = snmp_mibs[i];
-      }
-    }
-  }
-
-  return matched_mib;
-}
-
-static const struct snmp_mib *
-snmp_get_next_mib(const u32_t *oid, u8_t oid_len)
-{
-  u8_t i;
-  const struct snmp_mib *next_mib = NULL;
-
-  LWIP_ASSERT("'oid' param must not be NULL!", (oid != NULL));
-
-  if (oid_len == 0) {
-    return NULL;
-  }
-
-  for (i = 0; i < snmp_num_mibs; i++) {
-    if (snmp_mibs[i]->base_oid != NULL) {
-      /* check if mib is located behind starting point */
-      if (snmp_oid_compare(snmp_mibs[i]->base_oid, snmp_mibs[i]->base_oid_len, oid, oid_len) > 0) {
-        if ((next_mib == NULL) ||
-            (snmp_oid_compare(snmp_mibs[i]->base_oid, snmp_mibs[i]->base_oid_len,
-                              next_mib->base_oid, next_mib->base_oid_len) < 0)) {
-          next_mib = snmp_mibs[i];
-        }
-      }
-    }
-  }
-
-  return next_mib;
-}
-
-static const struct snmp_mib *
-snmp_get_mib_between(const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len)
-{
-  const struct snmp_mib *next_mib = snmp_get_next_mib(oid1, oid1_len);
-
-  LWIP_ASSERT("'oid2' param must not be NULL!", (oid2 != NULL));
-  LWIP_ASSERT("'oid2_len' param must be greater than 0!", (oid2_len > 0));
-
-  if (next_mib != NULL) {
-    if (snmp_oid_compare(next_mib->base_oid, next_mib->base_oid_len, oid2, oid2_len) < 0) {
-      return next_mib;
-    }
-  }
-
-  return NULL;
-}
-
-u8_t
-snmp_get_node_instance_from_oid(const u32_t *oid, u8_t oid_len, struct snmp_node_instance *node_instance)
-{
-  u8_t result = SNMP_ERR_NOSUCHOBJECT;
-  const struct snmp_mib *mib;
-  const struct snmp_node *mn = NULL;
-
-  mib = snmp_get_mib_from_oid(oid, oid_len);
-  if (mib != NULL) {
-    u8_t oid_instance_len;
-
-    mn = snmp_mib_tree_resolve_exact(mib, oid, oid_len, &oid_instance_len);
-    if ((mn != NULL) && (mn->node_type != SNMP_NODE_TREE)) {
-      /* get instance */
-      const struct snmp_leaf_node *leaf_node = (const struct snmp_leaf_node *)(const void *)mn;
-
-      node_instance->node = mn;
-      snmp_oid_assign(&node_instance->instance_oid, oid + (oid_len - oid_instance_len), oid_instance_len);
-
-      result = leaf_node->get_instance(
-                 oid,
-                 oid_len - oid_instance_len,
-                 node_instance);
-
-#ifdef LWIP_DEBUG
-      if (result == SNMP_ERR_NOERROR) {
-        if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_READ) != 0) && (node_instance->get_value == NULL)) {
-          LWIP_DEBUGF(SNMP_DEBUG, ("SNMP inconsistent access: node is readable but no get_value function is specified\n"));
-        }
-        if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_WRITE) != 0) && (node_instance->set_value == NULL)) {
-          LWIP_DEBUGF(SNMP_DEBUG, ("SNMP inconsistent access: node is writable but no set_value and/or set_test function is specified\n"));
-        }
-      }
-#endif
-    }
-  }
-
-  return result;
-}
-
-u8_t
-snmp_get_next_node_instance_from_oid(const u32_t *oid, u8_t oid_len, snmp_validate_node_instance_method validate_node_instance_method, void *validate_node_instance_arg, struct snmp_obj_id *node_oid, struct snmp_node_instance *node_instance)
-{
-  const struct snmp_mib      *mib;
-  const struct snmp_node *mn = NULL;
-  const u32_t *start_oid     = NULL;
-  u8_t         start_oid_len = 0;
-
-  /* resolve target MIB from passed OID */
-  mib = snmp_get_mib_from_oid(oid, oid_len);
-  if (mib == NULL) {
-    /* passed OID does not reference any known MIB, start at the next closest MIB */
-    mib = snmp_get_next_mib(oid, oid_len);
-
-    if (mib != NULL) {
-      start_oid     = mib->base_oid;
-      start_oid_len = mib->base_oid_len;
-    }
-  } else {
-    start_oid     = oid;
-    start_oid_len = oid_len;
-  }
-
-  /* resolve target node from MIB, skip to next MIB if no suitable node is found in current MIB */
-  while ((mib != NULL) && (mn == NULL)) {
-    u8_t oid_instance_len;
-
-    /* check if OID directly references a node inside current MIB, in this case we have to ask this node for the next instance */
-    mn = snmp_mib_tree_resolve_exact(mib, start_oid, start_oid_len, &oid_instance_len);
-    if (mn != NULL) {
-      snmp_oid_assign(node_oid, start_oid, start_oid_len - oid_instance_len); /* set oid to node */
-      snmp_oid_assign(&node_instance->instance_oid, start_oid + (start_oid_len - oid_instance_len), oid_instance_len); /* set (relative) instance oid */
-    } else {
-      /* OID does not reference a node, search for the next closest node inside MIB; set instance_oid.len to zero because we want the first instance of this node */
-      mn = snmp_mib_tree_resolve_next(mib, start_oid, start_oid_len, node_oid);
-      node_instance->instance_oid.len = 0;
-    }
-
-    /* validate the node; if the node has no further instance or the returned instance is invalid, search for the next in MIB and validate again */
-    node_instance->node = mn;
-    while (mn != NULL) {
-      u8_t result;
-
-      /* clear fields which may have values from previous loops */
-      node_instance->asn1_type        = 0;
-      node_instance->access           = SNMP_NODE_INSTANCE_NOT_ACCESSIBLE;
-      node_instance->get_value        = NULL;
-      node_instance->set_test         = NULL;
-      node_instance->set_value        = NULL;
-      node_instance->release_instance = NULL;
-      node_instance->reference.ptr    = NULL;
-      node_instance->reference_len    = 0;
-
-      result = ((const struct snmp_leaf_node *)(const void *)mn)->get_next_instance(
-                 node_oid->id,
-                 node_oid->len,
-                 node_instance);
-
-      if (result == SNMP_ERR_NOERROR) {
-#ifdef LWIP_DEBUG
-        if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_READ) != 0) && (node_instance->get_value == NULL)) {
-          LWIP_DEBUGF(SNMP_DEBUG, ("SNMP inconsistent access: node is readable but no get_value function is specified\n"));
-        }
-        if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_WRITE) != 0) && (node_instance->set_value == NULL)) {
-          LWIP_DEBUGF(SNMP_DEBUG, ("SNMP inconsistent access: node is writable but no set_value function is specified\n"));
-        }
-#endif
-
-        /* validate node because the node may be not accessible for example (but let the caller decide what is valid */
-        if ((validate_node_instance_method == NULL) ||
-            (validate_node_instance_method(node_instance, validate_node_instance_arg) == SNMP_ERR_NOERROR)) {
-          /* node_oid "returns" the full result OID (including the instance part) */
-          snmp_oid_append(node_oid, node_instance->instance_oid.id, node_instance->instance_oid.len);
-          break;
-        }
-
-        if (node_instance->release_instance != NULL) {
-          node_instance->release_instance(node_instance);
-        }
-        /*
-        the instance itself is not valid, ask for next instance from same node.
-        we don't have to change any variables because node_instance->instance_oid is used as input (starting point)
-        as well as output (resulting next OID), so we have to simply call get_next_instance method again
-        */
-      } else {
-        if (node_instance->release_instance != NULL) {
-          node_instance->release_instance(node_instance);
-        }
-
-        /* the node has no further instance, skip to next node */
-        mn = snmp_mib_tree_resolve_next(mib, node_oid->id, node_oid->len, &node_instance->instance_oid); /* misuse node_instance->instance_oid as tmp buffer */
-        if (mn != NULL) {
-          /* prepare for next loop */
-          snmp_oid_assign(node_oid, node_instance->instance_oid.id, node_instance->instance_oid.len);
-          node_instance->instance_oid.len = 0;
-          node_instance->node = mn;
-        }
-      }
-    }
-
-    if (mn != NULL) {
-      /*
-      we found a suitable next node,
-      now we have to check if a inner MIB is located between the searched OID and the resulting OID.
-      this is possible because MIB's may be located anywhere in the global tree, that means also in
-      the subtree of another MIB (e.g. if searched OID is .2 and resulting OID is .4, then another
-      MIB having .3 as root node may exist)
-      */
-      const struct snmp_mib *intermediate_mib;
-      intermediate_mib = snmp_get_mib_between(start_oid, start_oid_len, node_oid->id, node_oid->len);
-
-      if (intermediate_mib != NULL) {
-        /* search for first node inside intermediate mib in next loop */
-        if (node_instance->release_instance != NULL) {
-          node_instance->release_instance(node_instance);
-        }
-
-        mn            = NULL;
-        mib           = intermediate_mib;
-        start_oid     = mib->base_oid;
-        start_oid_len = mib->base_oid_len;
-      }
-      /* else { we found out target node } */
-    } else {
-      /*
-      there is no further (suitable) node inside this MIB, search for the next MIB with following priority
-      1. search for inner MIB's (whose root is located inside tree of current MIB)
-      2. search for surrouding MIB's (where the current MIB is the inner MIB) and continue there if any
-      3. take the next closest MIB (not being related to the current MIB)
-      */
-      const struct snmp_mib *next_mib;
-      next_mib = snmp_get_next_mib(start_oid, start_oid_len); /* returns MIB's related to point 1 and 3 */
-
-      /* is the found MIB an inner MIB? (point 1) */
-      if ((next_mib != NULL) && (next_mib->base_oid_len > mib->base_oid_len) &&
-          (snmp_oid_compare(next_mib->base_oid, mib->base_oid_len, mib->base_oid, mib->base_oid_len) == 0)) {
-        /* yes it is -> continue at inner MIB */
-        mib = next_mib;
-        start_oid     = mib->base_oid;
-        start_oid_len = mib->base_oid_len;
-      } else {
-        /* check if there is a surrounding mib where to continue (point 2) (only possible if OID length > 1) */
-        if (mib->base_oid_len > 1) {
-          mib = snmp_get_mib_from_oid(mib->base_oid, mib->base_oid_len - 1);
-
-          if (mib == NULL) {
-            /* no surrounding mib, use next mib encountered above (point 3) */
-            mib = next_mib;
-
-            if (mib != NULL) {
-              start_oid     = mib->base_oid;
-              start_oid_len = mib->base_oid_len;
-            }
-          }
-          /* else { start_oid stays the same because we want to continue from current offset in surrounding mib (point 2) } */
-        }
-      }
-    }
-  }
-
-  if (mib == NULL) {
-    /* loop is only left when mib == null (error) or mib_node != NULL (success) */
-    return SNMP_ERR_ENDOFMIBVIEW;
-  }
-
-  return SNMP_ERR_NOERROR;
-}
-
-/**
- * Searches tree for the supplied object identifier.
- *
- */
-const struct snmp_node *
-snmp_mib_tree_resolve_exact(const struct snmp_mib *mib, const u32_t *oid, u8_t oid_len, u8_t *oid_instance_len)
-{
-  const struct snmp_node *const *node = &mib->root_node;
-  u8_t oid_offset = mib->base_oid_len;
-
-  while ((oid_offset < oid_len) && ((*node)->node_type == SNMP_NODE_TREE)) {
-    /* search for matching sub node */
-    u32_t subnode_oid = *(oid + oid_offset);
-
-    u32_t i = (*(const struct snmp_tree_node * const *)node)->subnode_count;
-    node    = (*(const struct snmp_tree_node * const *)node)->subnodes;
-    while ((i > 0) && ((*node)->oid != subnode_oid)) {
-      node++;
-      i--;
-    }
-
-    if (i == 0) {
-      /* no matching subnode found */
-      return NULL;
-    }
-
-    oid_offset++;
-  }
-
-  if ((*node)->node_type != SNMP_NODE_TREE) {
-    /* we found a leaf node */
-    *oid_instance_len = oid_len - oid_offset;
-    return (*node);
-  }
-
-  return NULL;
-}
-
-const struct snmp_node *
-snmp_mib_tree_resolve_next(const struct snmp_mib *mib, const u32_t *oid, u8_t oid_len, struct snmp_obj_id *oidret)
-{
-  u8_t  oid_offset = mib->base_oid_len;
-  const struct snmp_node *const *node;
-  const struct snmp_tree_node *node_stack[SNMP_MAX_OBJ_ID_LEN];
-  s32_t nsi = 0; /* NodeStackIndex */
-  u32_t subnode_oid;
-
-  if (mib->root_node->node_type != SNMP_NODE_TREE) {
-    /* a next operation on a mib with only a leaf node will always return NULL because there is no other node */
-    return NULL;
-  }
-
-  /* first build node stack related to passed oid (as far as possible), then go backwards to determine the next node */
-  node_stack[nsi] = (const struct snmp_tree_node *)(const void *)mib->root_node;
-  while (oid_offset < oid_len) {
-    /* search for matching sub node */
-    u32_t i = node_stack[nsi]->subnode_count;
-    node    = node_stack[nsi]->subnodes;
-
-    subnode_oid = *(oid + oid_offset);
-
-    while ((i > 0) && ((*node)->oid != subnode_oid)) {
-      node++;
-      i--;
-    }
-
-    if ((i == 0) || ((*node)->node_type != SNMP_NODE_TREE)) {
-      /* no (matching) tree-subnode found */
-      break;
-    }
-    nsi++;
-    node_stack[nsi] = (const struct snmp_tree_node *)(const void *)(*node);
-
-    oid_offset++;
-  }
-
-
-  if (oid_offset >= oid_len) {
-    /* passed oid references a tree node -> return first useable sub node of it */
-    subnode_oid = 0;
-  } else {
-    subnode_oid = *(oid + oid_offset) + 1;
-  }
-
-  while (nsi >= 0) {
-    const struct snmp_node *subnode = NULL;
-
-    /* find next node on current level */
-    s32_t i        = node_stack[nsi]->subnode_count;
-    node           = node_stack[nsi]->subnodes;
-    while (i > 0) {
-      if ((*node)->oid == subnode_oid) {
-        subnode = *node;
-        break;
-      } else if (((*node)->oid > subnode_oid) && ((subnode == NULL) || ((*node)->oid < subnode->oid))) {
-        subnode = *node;
-      }
-
-      node++;
-      i--;
-    }
-
-    if (subnode == NULL) {
-      /* no further node found on this level, go one level up and start searching with index of current node*/
-      subnode_oid = node_stack[nsi]->node.oid + 1;
-      nsi--;
-    } else {
-      if (subnode->node_type == SNMP_NODE_TREE) {
-        /* next is a tree node, go into it and start searching */
-        nsi++;
-        node_stack[nsi] = (const struct snmp_tree_node *)(const void *)subnode;
-        subnode_oid = 0;
-      } else {
-        /* we found a leaf node -> fill oidret and return it */
-        snmp_oid_assign(oidret, mib->base_oid, mib->base_oid_len);
-        i = 1;
-        while (i <= nsi) {
-          oidret->id[oidret->len] = node_stack[i]->node.oid;
-          oidret->len++;
-          i++;
-        }
-
-        oidret->id[oidret->len] = subnode->oid;
-        oidret->len++;
-
-        return subnode;
-      }
-    }
-  }
-
-  return NULL;
-}
-
-/** initialize struct next_oid_state using this function before passing it to next_oid_check */
-void
-snmp_next_oid_init(struct snmp_next_oid_state *state,
-                   const u32_t *start_oid, u8_t start_oid_len,
-                   u32_t *next_oid_buf, u8_t next_oid_max_len)
-{
-  state->start_oid        = start_oid;
-  state->start_oid_len    = start_oid_len;
-  state->next_oid         = next_oid_buf;
-  state->next_oid_len     = 0;
-  state->next_oid_max_len = next_oid_max_len;
-  state->status           = SNMP_NEXT_OID_STATUS_NO_MATCH;
-}
-
-/** checks if the passed incomplete OID may be a possible candidate for snmp_next_oid_check();
-this methid is intended if the complete OID is not yet known but it is very expensive to build it up,
-so it is possible to test the starting part before building up the complete oid and pass it to snmp_next_oid_check()*/
-u8_t
-snmp_next_oid_precheck(struct snmp_next_oid_state *state, const u32_t *oid, u8_t oid_len)
-{
-  if (state->status != SNMP_NEXT_OID_STATUS_BUF_TO_SMALL) {
-    u8_t start_oid_len = (oid_len < state->start_oid_len) ? oid_len : state->start_oid_len;
-
-    /* check passed OID is located behind start offset */
-    if (snmp_oid_compare(oid, oid_len, state->start_oid, start_oid_len) >= 0) {
-      /* check if new oid is located closer to start oid than current closest oid */
-      if ((state->status == SNMP_NEXT_OID_STATUS_NO_MATCH) ||
-          (snmp_oid_compare(oid, oid_len, state->next_oid, state->next_oid_len) < 0)) {
-        return 1;
-      }
-    }
-  }
-
-  return 0;
-}
-
-/** checks the passed OID if it is a candidate to be the next one (get_next); returns !=0 if passed oid is currently closest, otherwise 0 */
-u8_t
-snmp_next_oid_check(struct snmp_next_oid_state *state, const u32_t *oid, u8_t oid_len, void *reference)
-{
-  /* do not overwrite a fail result */
-  if (state->status != SNMP_NEXT_OID_STATUS_BUF_TO_SMALL) {
-    /* check passed OID is located behind start offset */
-    if (snmp_oid_compare(oid, oid_len, state->start_oid, state->start_oid_len) > 0) {
-      /* check if new oid is located closer to start oid than current closest oid */
-      if ((state->status == SNMP_NEXT_OID_STATUS_NO_MATCH) ||
-          (snmp_oid_compare(oid, oid_len, state->next_oid, state->next_oid_len) < 0)) {
-        if (oid_len <= state->next_oid_max_len) {
-          MEMCPY(state->next_oid, oid, oid_len * sizeof(u32_t));
-          state->next_oid_len = oid_len;
-          state->status       = SNMP_NEXT_OID_STATUS_SUCCESS;
-          state->reference    = reference;
-          return 1;
-        } else {
-          state->status = SNMP_NEXT_OID_STATUS_BUF_TO_SMALL;
-        }
-      }
-    }
-  }
-
-  return 0;
-}
-
-u8_t
-snmp_oid_in_range(const u32_t *oid_in, u8_t oid_len, const struct snmp_oid_range *oid_ranges, u8_t oid_ranges_len)
-{
-  u8_t i;
-
-  if (oid_len != oid_ranges_len) {
-    return 0;
-  }
-
-  for (i = 0; i < oid_ranges_len; i++) {
-    if ((oid_in[i] < oid_ranges[i].min) || (oid_in[i] > oid_ranges[i].max)) {
-      return 0;
-    }
-  }
-
-  return 1;
-}
-
-snmp_err_t
-snmp_set_test_ok(struct snmp_node_instance *instance, u16_t value_len, void *value)
-{
-  LWIP_UNUSED_ARG(instance);
-  LWIP_UNUSED_ARG(value_len);
-  LWIP_UNUSED_ARG(value);
-
-  return SNMP_ERR_NOERROR;
-}
-
-/**
- * Decodes BITS pseudotype value from ASN.1 OctetString.
- *
- * @note Because BITS pseudo type is encoded as OCTET STRING, it cannot directly
- * be encoded/decoded by the agent. Instead call this function as required from
- * get/test/set methods.
- *
- * @param buf points to a buffer holding the ASN1 octet string
- * @param buf_len length of octet string
- * @param bit_value decoded Bit value with Bit0 == LSB
- * @return ERR_OK if successful, ERR_ARG if bit value contains more than 32 bit
- */
-err_t
-snmp_decode_bits(const u8_t *buf, u32_t buf_len, u32_t *bit_value)
-{
-  u8_t b;
-  u8_t bits_processed = 0;
-  *bit_value = 0;
-
-  while (buf_len > 0) {
-    /* any bit set in this byte? */
-    if (*buf != 0x00) {
-      if (bits_processed >= 32) {
-        /* accept more than 4 bytes, but only when no bits are set */
-        return ERR_VAL;
-      }
-
-      b = *buf;
-      do {
-        if (b & 0x80) {
-          *bit_value |= (1 << bits_processed);
-        }
-        bits_processed++;
-        b <<= 1;
-      } while ((bits_processed & 0x07) != 0); /* &0x07 -> % 8 */
-    } else {
-      bits_processed += 8;
-    }
-
-    buf_len--;
-    buf++;
-  }
-
-  return ERR_OK;
-}
-
-err_t
-snmp_decode_truthvalue(const s32_t *asn1_value, u8_t *bool_value)
-{
-  /* defined by RFC1443:
-   TruthValue ::= TEXTUAL-CONVENTION
-    STATUS       current
-    DESCRIPTION
-     "Represents a boolean value."
-    SYNTAX       INTEGER { true(1), false(2) }
-  */
-
-  if ((asn1_value == NULL) || (bool_value == NULL)) {
-    return ERR_ARG;
-  }
-
-  if (*asn1_value == 1) {
-    *bool_value = 1;
-  } else if (*asn1_value == 2) {
-    *bool_value = 0;
-  } else {
-    return ERR_VAL;
-  }
-
-  return ERR_OK;
-}
-
-/**
- * Encodes BITS pseudotype value into ASN.1 OctetString.
- *
- * @note Because BITS pseudo type is encoded as OCTET STRING, it cannot directly
- * be encoded/decoded by the agent. Instead call this function as required from
- * get/test/set methods.
- *
- * @param buf points to a buffer where the resulting ASN1 octet string is stored to
- * @param buf_len max length of the bufffer
- * @param bit_value Bit value to encode with Bit0 == LSB
- * @param bit_count Number of possible bits for the bit value (according to rfc we have to send all bits independant from their truth value)
- * @return number of bytes used from buffer to store the resulting OctetString
- */
-u8_t
-snmp_encode_bits(u8_t *buf, u32_t buf_len, u32_t bit_value, u8_t bit_count)
-{
-  u8_t len = 0;
-  u8_t min_bytes = (bit_count + 7) >> 3; /* >>3 -> / 8 */
-
-  while ((buf_len > 0) && (bit_value != 0x00)) {
-    s8_t i = 7;
-    *buf = 0x00;
-    while (i >= 0) {
-      if (bit_value & 0x01) {
-        *buf |= 0x01;
-      }
-
-      if (i > 0) {
-        *buf <<= 1;
-      }
-
-      bit_value >>= 1;
-      i--;
-    }
-
-    buf++;
-    buf_len--;
-    len++;
-  }
-
-  if (len < min_bytes) {
-    buf     += len;
-    buf_len -= len;
-
-    while ((len < min_bytes) && (buf_len > 0)) {
-      *buf = 0x00;
-      buf++;
-      buf_len--;
-      len++;
-    }
-  }
-
-  return len;
-}
-
-u8_t
-snmp_encode_truthvalue(s32_t *asn1_value, u32_t bool_value)
-{
-  /* defined by RFC1443:
-   TruthValue ::= TEXTUAL-CONVENTION
-    STATUS       current
-    DESCRIPTION
-     "Represents a boolean value."
-    SYNTAX       INTEGER { true(1), false(2) }
-  */
-
-  if (asn1_value == NULL) {
-    return 0;
-  }
-
-  if (bool_value) {
-    *asn1_value = 1; /* defined by RFC1443 */
-  } else {
-    *asn1_value = 2; /* defined by RFC1443 */
-  }
-
-  return sizeof(s32_t);
-}
-
-#endif /* LWIP_SNMP */
+/**
+ * @file
+ * MIB tree access/construction functions.
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Christiaan Simons <christiaan.simons@axon.tv>
+ *         Martin Hentschel <info@cl-soft.de>
+*/
+
+/**
+ * @defgroup snmp SNMPv2c agent
+ * @ingroup apps
+ * SNMPv2c compatible agent\n
+ * There is also a MIB compiler and a MIB viewer in lwIP contrib repository
+ * (lwip-contrib/apps/LwipMibCompiler).\n
+ * The agent implements the most important MIB2 MIBs including IPv6 support
+ * (interfaces, UDP, TCP, SNMP, ICMP, SYSTEM). IP MIB is an older version
+ * whithout IPv6 statistics (TODO).\n
+ * Rewritten by Martin Hentschel <info@cl-soft.de> and
+ * Dirk Ziegelmeier <dziegel@gmx.de>\n
+ * Work on SNMPv3 has started, but is not finished.\n
+ *
+ * 0 Agent Capabilities
+ * ====================
+ * 
+ * Features:
+ * ---------
+ * - SNMPv2c support.
+ * - Low RAM usage - no memory pools, stack only.
+ * - MIB2 implementation is separated from SNMP stack.
+ * - Support for multiple MIBs (snmp_set_mibs() call) - e.g. for private MIB.
+ * - Simple and generic API for MIB implementation.
+ * - Comfortable node types and helper functions for scalar arrays and tables.
+ * - Counter64, bit and truthvalue datatype support.
+ * - Callbacks for SNMP writes e.g. to implement persistency.
+ * - Runs on two APIs: RAW and netconn.
+ * - Async API is gone - the stack now supports netconn API instead,
+ *   so blocking operations can be done in MIB calls.
+ *   SNMP runs in a worker thread when netconn API is used.
+ * - Simplified thread sync support for MIBs - useful when MIBs
+ *   need to access variables shared with other threads where no locking is
+ *   possible. Used in MIB2 to access lwIP stats from lwIP thread.
+ * 
+ * MIB compiler (code generator):
+ * ------------------------------
+ * - Provided in lwIP contrib repository.
+ * - Written in C#. MIB viewer used Windows Forms.
+ * - Developed on Windows with Visual Studio 2010.
+ * - Can be compiled and used on all platforms with http://www.monodevelop.com/.
+ * - Based on a heavily modified version of of SharpSnmpLib (a4bd05c6afb4)
+ *   (https://sharpsnmplib.codeplex.com/SourceControl/network/forks/Nemo157/MIBParserUpdate).
+ * - MIB parser, C file generation framework and LWIP code generation are cleanly
+ *   separated, which means the code may be useful as a base for code generation
+ *   of other SNMP agents.
+ * 
+ * Notes:
+ * ------
+ * - Stack and MIB compiler were used to implement a Profinet device.
+ *   Compiled/implemented MIBs: LLDP-MIB, LLDP-EXT-DOT3-MIB, LLDP-EXT-PNO-MIB.
+ * 
+ * SNMPv1 per RFC1157 and SNMPv2c per RFC 3416
+ * -------------------------------------------
+ *   Note the S in SNMP stands for "Simple". Note that "Simple" is
+ *   relative. SNMP is simple compared to the complex ISO network
+ *   management protocols CMIP (Common Management Information Protocol)
+ *   and CMOT (CMip Over Tcp).
+ * 
+ * MIB II
+ * ------
+ *   The standard lwIP stack management information base.
+ *   This is a required MIB, so this is always enabled.
+ *   The groups EGP, CMOT and transmission are disabled by default.
+ * 
+ *   Most mib-2 objects are not writable except:
+ *   sysName, sysLocation, sysContact, snmpEnableAuthenTraps.
+ *   Writing to or changing the ARP and IP address and route
+ *   tables is not possible.
+ * 
+ *   Note lwIP has a very limited notion of IP routing. It currently
+ *   doen't have a route table and doesn't have a notion of the U,G,H flags.
+ *   Instead lwIP uses the interface list with only one default interface
+ *   acting as a single gateway interface (G) for the default route.
+ * 
+ *   The agent returns a "virtual table" with the default route 0.0.0.0
+ *   for the default interface and network routes (no H) for each
+ *   network interface in the netif_list.
+ *   All routes are considered to be up (U).
+ * 
+ * Loading additional MIBs
+ * -----------------------
+ *   MIBs can only be added in compile-time, not in run-time.
+ *  
+ * 
+ * 1 Building the Agent
+ * ====================
+ * First of all you'll need to add the following define
+ * to your local lwipopts.h:
+ * \#define LWIP_SNMP               1
+ * 
+ * and add the source files your makefile.
+ * 
+ * Note you'll might need to adapt you network driver to update
+ * the mib2 variables for your interface.
+ * 
+ * 2 Running the Agent
+ * ===================
+ * The following function calls must be made in your program to
+ * actually get the SNMP agent running.
+ * 
+ * Before starting the agent you should supply pointers
+ * for sysContact, sysLocation, and snmpEnableAuthenTraps.
+ * You can do this by calling
+ * 
+ * - snmp_mib2_set_syscontact()
+ * - snmp_mib2_set_syslocation()
+ * - snmp_set_auth_traps_enabled()
+ * 
+ * You can register a callback which is called on successful write access: 
+ * snmp_set_write_callback().
+ * 
+ * Additionally you may want to set
+ * 
+ * - snmp_mib2_set_sysdescr()
+ * - snmp_set_device_enterprise_oid()
+ * - snmp_mib2_set_sysname()
+ * 
+ * Also before starting the agent you need to setup
+ * one or more trap destinations using these calls:
+ * 
+ * - snmp_trap_dst_enable()
+ * - snmp_trap_dst_ip_set()
+ * 
+ * If you need more than MIB2, set the MIBs you want to use
+ * by snmp_set_mibs().
+ * 
+ * Finally, enable the agent by calling snmp_init()
+ *
+ * @defgroup snmp_core Core
+ * @ingroup snmp
+ * 
+ * @defgroup snmp_traps Traps
+ * @ingroup snmp
+ */
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "snmp_core_priv.h"
+#include "lwip/netif.h"
+#include <string.h>
+
+
+#if (LWIP_SNMP && (SNMP_TRAP_DESTINATIONS<=0))
+  #error "If you want to use SNMP, you have to define SNMP_TRAP_DESTINATIONS>=1 in your lwipopts.h"
+#endif
+#if (!LWIP_UDP && LWIP_SNMP)
+  #error "If you want to use SNMP, you have to define LWIP_UDP=1 in your lwipopts.h"
+#endif
+
+struct snmp_statistics snmp_stats;
+static const struct snmp_obj_id  snmp_device_enterprise_oid_default = {SNMP_DEVICE_ENTERPRISE_OID_LEN, SNMP_DEVICE_ENTERPRISE_OID};
+static const struct snmp_obj_id* snmp_device_enterprise_oid         = &snmp_device_enterprise_oid_default;
+
+const u32_t snmp_zero_dot_zero_values[] = { 0, 0 };
+const struct snmp_obj_id_const_ref snmp_zero_dot_zero = { LWIP_ARRAYSIZE(snmp_zero_dot_zero_values), snmp_zero_dot_zero_values };
+
+
+#if SNMP_LWIP_MIB2
+#include "lwip/apps/snmp_mib2.h"
+static const struct snmp_mib* const default_mibs[] = { &mib2 };
+static u8_t snmp_num_mibs                          = 1;
+#else
+static const struct snmp_mib* const default_mibs[] = { NULL };
+static u8_t snmp_num_mibs                          = 0;
+#endif
+
+/* List of known mibs */
+static struct snmp_mib const * const *snmp_mibs = default_mibs;
+
+/**
+ * @ingroup snmp_core
+ * Sets the MIBs to use.
+ * Example: call snmp_set_mibs() as follows:
+ * static const struct snmp_mib *my_snmp_mibs[] = {
+ *   &mib2,
+ *   &private_mib
+ * };
+ * snmp_set_mibs(my_snmp_mibs, LWIP_ARRAYSIZE(my_snmp_mibs));
+ */
+void
+snmp_set_mibs(const struct snmp_mib **mibs, u8_t num_mibs)
+{
+  LWIP_ASSERT("mibs pointer must be != NULL", (mibs != NULL));
+  LWIP_ASSERT("num_mibs pointer must be != 0", (num_mibs != 0));
+  snmp_mibs     = mibs;
+  snmp_num_mibs = num_mibs;
+}
+
+/**
+ * @ingroup snmp_core
+ * 'device enterprise oid' is used for 'device OID' field in trap PDU's (for identification of generating device)
+ * as well as for value returned by MIB-2 'sysObjectID' field (if internal MIB2 implementation is used).
+ * The 'device enterprise oid' shall point to an OID located under 'private-enterprises' branch (1.3.6.1.4.1.XXX). If a vendor
+ * wants to provide a custom object there, he has to get its own enterprise oid from IANA (http://www.iana.org). It
+ * is not allowed to use LWIP enterprise ID!
+ * In order to identify a specific device it is recommended to create a dedicated OID for each device type under its own 
+ * enterprise oid.
+ * e.g.
+ * device a > 1.3.6.1.4.1.XXX(ent-oid).1(devices).1(device a)
+ * device b > 1.3.6.1.4.1.XXX(ent-oid).1(devices).2(device b)
+ * for more details see description of 'sysObjectID' field in RFC1213-MIB
+ */
+void snmp_set_device_enterprise_oid(const struct snmp_obj_id* device_enterprise_oid)
+{
+  if (device_enterprise_oid == NULL) {
+    snmp_device_enterprise_oid = &snmp_device_enterprise_oid_default;
+  } else {
+    snmp_device_enterprise_oid = device_enterprise_oid;
+  }
+}
+
+/**
+ * @ingroup snmp_core
+ * Get 'device enterprise oid' 
+ */
+const struct snmp_obj_id* snmp_get_device_enterprise_oid(void)
+{
+  return snmp_device_enterprise_oid;
+}
+
+#if LWIP_IPV4
+/**
+ * Conversion from InetAddressIPv4 oid to lwIP ip4_addr
+ * @param oid points to u32_t ident[4] input
+ * @param ip points to output struct
+ */
+u8_t
+snmp_oid_to_ip4(const u32_t *oid, ip4_addr_t *ip)
+{
+  if ((oid[0] > 0xFF) ||
+      (oid[1] > 0xFF) ||
+      (oid[2] > 0xFF) ||
+      (oid[3] > 0xFF)) {
+    ip4_addr_copy(*ip, *IP4_ADDR_ANY4);
+    return 0;
+  }
+
+  IP4_ADDR(ip, oid[0], oid[1], oid[2], oid[3]);
+  return 1;
+}
+
+/**
+ * Convert ip4_addr to InetAddressIPv4 (no InetAddressType)
+ * @param ip points to input struct
+ * @param oid points to u32_t ident[4] output
+ */
+void
+snmp_ip4_to_oid(const ip4_addr_t *ip, u32_t *oid)
+{
+  oid[0] = ip4_addr1(ip);
+  oid[1] = ip4_addr2(ip);
+  oid[2] = ip4_addr3(ip);
+  oid[3] = ip4_addr4(ip);
+}
+#endif /* LWIP_IPV4 */
+
+#if LWIP_IPV6
+/**
+ * Conversion from InetAddressIPv6 oid to lwIP ip6_addr
+ * @param oid points to u32_t oid[16] input
+ * @param ip points to output struct
+ */
+u8_t
+snmp_oid_to_ip6(const u32_t *oid, ip6_addr_t *ip)
+{
+  if ((oid[0]  > 0xFF) ||
+      (oid[1]  > 0xFF) ||
+      (oid[2]  > 0xFF) ||
+      (oid[3]  > 0xFF) ||
+      (oid[4]  > 0xFF) ||
+      (oid[5]  > 0xFF) ||
+      (oid[6]  > 0xFF) ||
+      (oid[7]  > 0xFF) ||
+      (oid[8]  > 0xFF) ||
+      (oid[9]  > 0xFF) ||
+      (oid[10] > 0xFF) ||
+      (oid[11] > 0xFF) ||
+      (oid[12] > 0xFF) ||
+      (oid[13] > 0xFF) ||
+      (oid[14] > 0xFF) ||
+      (oid[15] > 0xFF)) {
+    ip6_addr_set_any(ip);
+    return 0;
+  }
+
+  ip->addr[0] = (oid[0]  << 24) | (oid[1]  << 16) | (oid[2]  << 8) | (oid[3]  << 0);
+  ip->addr[1] = (oid[4]  << 24) | (oid[5]  << 16) | (oid[6]  << 8) | (oid[7]  << 0);
+  ip->addr[2] = (oid[8]  << 24) | (oid[9]  << 16) | (oid[10] << 8) | (oid[11] << 0);
+  ip->addr[3] = (oid[12] << 24) | (oid[13] << 16) | (oid[14] << 8) | (oid[15] << 0);
+  return 1;
+}
+
+/**
+ * Convert ip6_addr to InetAddressIPv6 (no InetAddressType)
+ * @param ip points to input struct
+ * @param oid points to u32_t ident[16] output
+ */
+void
+snmp_ip6_to_oid(const ip6_addr_t *ip, u32_t *oid)
+{
+  oid[0]  = (ip->addr[0] & 0xFF000000) >> 24;
+  oid[1]  = (ip->addr[0] & 0x00FF0000) >> 16;
+  oid[2]  = (ip->addr[0] & 0x0000FF00) >>  8;
+  oid[3]  = (ip->addr[0] & 0x000000FF) >>  0;
+  oid[4]  = (ip->addr[1] & 0xFF000000) >> 24;
+  oid[5]  = (ip->addr[1] & 0x00FF0000) >> 16;
+  oid[6]  = (ip->addr[1] & 0x0000FF00) >>  8;
+  oid[7]  = (ip->addr[1] & 0x000000FF) >>  0;
+  oid[8]  = (ip->addr[2] & 0xFF000000) >> 24;
+  oid[9]  = (ip->addr[2] & 0x00FF0000) >> 16;
+  oid[10] = (ip->addr[2] & 0x0000FF00) >>  8;
+  oid[11] = (ip->addr[2] & 0x000000FF) >>  0;
+  oid[12] = (ip->addr[3] & 0xFF000000) >> 24;
+  oid[13] = (ip->addr[3] & 0x00FF0000) >> 16;
+  oid[14] = (ip->addr[3] & 0x0000FF00) >>  8;
+  oid[15] = (ip->addr[3] & 0x000000FF) >>  0;
+}
+#endif /* LWIP_IPV6 */
+
+#if LWIP_IPV4 || LWIP_IPV6
+/**
+ * Convert to InetAddressType+InetAddress+InetPortNumber
+ * @param ip IP address
+ * @param port Port
+ * @param oid OID
+ * @return OID length
+ */
+u8_t
+snmp_ip_port_to_oid(const ip_addr_t *ip, u16_t port, u32_t *oid)
+{
+  u8_t idx;
+
+  idx = snmp_ip_to_oid(ip, oid);
+  oid[idx] = port;
+  idx++;
+
+  return idx;
+}
+
+/**
+ * Convert to InetAddressType+InetAddress
+ * @param ip IP address
+ * @param oid OID
+ * @return OID length
+ */
+u8_t
+snmp_ip_to_oid(const ip_addr_t *ip, u32_t *oid)
+{
+  if (IP_IS_ANY_TYPE_VAL(*ip)) {
+    oid[0] = 0; /* any */
+    oid[1] = 0; /* no IP OIDs follow */
+    return 2;
+  } else if (IP_IS_V6(ip)) {
+#if LWIP_IPV6
+    oid[0] = 2; /* ipv6 */
+    oid[1] = 16; /* 16 InetAddressIPv6 OIDs follow */
+    snmp_ip6_to_oid(ip_2_ip6(ip), &oid[2]);
+    return 18;
+#else /* LWIP_IPV6 */
+    return 0;
+#endif /* LWIP_IPV6 */
+  } else {
+#if LWIP_IPV4
+    oid[0] = 1; /* ipv4 */
+    oid[1] = 4; /* 4 InetAddressIPv4 OIDs follow */
+    snmp_ip4_to_oid(ip_2_ip4(ip), &oid[2]);
+    return 6;
+#else /* LWIP_IPV4 */
+    return 0;
+#endif /* LWIP_IPV4 */
+  }
+}
+
+/**
+ * Convert from InetAddressType+InetAddress to ip_addr_t
+ * @param oid OID
+ * @param oid_len OID length
+ * @param ip IP address
+ * @return Parsed OID length
+ */
+u8_t
+snmp_oid_to_ip(const u32_t *oid, u8_t oid_len, ip_addr_t *ip)
+{
+  /* InetAddressType */
+  if (oid_len < 1) {
+    return 0;
+  }
+
+  if (oid[0] == 0) { /* any */
+    /* 1x InetAddressType, 1x OID len */
+    if (oid_len < 2) {
+      return 0;
+    }
+    if (oid[1] != 0) {
+      return 0;
+    }
+
+    memset(ip, 0, sizeof(*ip));
+    IP_SET_TYPE(ip, IPADDR_TYPE_ANY);
+
+    return 2;
+  } else if (oid[0] == 1) { /* ipv4 */
+#if LWIP_IPV4
+    /* 1x InetAddressType, 1x OID len, 4x InetAddressIPv4 */
+    if (oid_len < 6) {
+      return 0;
+    }
+
+    /* 4x ipv4 OID */
+    if (oid[1] != 4) {
+      return 0;
+    }
+
+    IP_SET_TYPE(ip, IPADDR_TYPE_V4);
+    if (!snmp_oid_to_ip4(&oid[2], ip_2_ip4(ip))) {
+      return 0;
+    }
+
+    return 6;
+#else /* LWIP_IPV4 */
+    return 0;
+#endif /* LWIP_IPV4 */
+  } else if (oid[0] == 2) { /* ipv6 */
+#if LWIP_IPV6
+    /* 1x InetAddressType, 1x OID len, 16x InetAddressIPv6 */
+    if (oid_len < 18) {
+      return 0;
+    }
+
+    /* 16x ipv6 OID */
+    if (oid[1] != 16) {
+      return 0;
+    }
+
+    IP_SET_TYPE(ip, IPADDR_TYPE_V6);
+    if (!snmp_oid_to_ip6(&oid[2], ip_2_ip6(ip))) {
+      return 0;
+    }
+
+    return 18;
+#else /* LWIP_IPV6 */
+    return 0;
+#endif /* LWIP_IPV6 */
+  } else { /* unsupported InetAddressType */
+    return 0;
+  }
+}
+
+/**
+ * Convert from InetAddressType+InetAddress+InetPortNumber to ip_addr_t and u16_t
+ * @param oid OID
+ * @param oid_len OID length
+ * @param ip IP address
+ * @param port Port
+ * @return Parsed OID length
+ */
+u8_t
+snmp_oid_to_ip_port(const u32_t *oid, u8_t oid_len, ip_addr_t *ip, u16_t *port)
+{
+  u8_t idx = 0;
+
+  /* InetAddressType + InetAddress */
+  idx += snmp_oid_to_ip(&oid[idx], oid_len-idx, ip);
+  if (idx == 0) {
+    return 0;
+  }
+
+  /* InetPortNumber */
+  if (oid_len < (idx+1)) {
+    return 0;
+  }
+  if (oid[idx] > 0xffff) {
+    return 0;
+  }
+  *port = (u16_t)oid[idx];
+  idx++;
+
+  return idx;
+}
+
+#endif /* LWIP_IPV4 || LWIP_IPV6 */
+
+/**
+ * Assign an OID to struct snmp_obj_id
+ * @param target Assignment target 
+ * @param oid OID
+ * @param oid_len OID length
+ */
+void
+snmp_oid_assign(struct snmp_obj_id* target, const u32_t *oid, u8_t oid_len)
+{
+  LWIP_ASSERT("oid_len <= LWIP_SNMP_OBJ_ID_LEN", oid_len <= SNMP_MAX_OBJ_ID_LEN);
+
+  target->len = oid_len;
+
+  if (oid_len > 0) {
+    MEMCPY(target->id, oid, oid_len * sizeof(u32_t));
+  }
+}
+
+/**
+ * Prefix an OID to OID in struct snmp_obj_id
+ * @param target Assignment target to prefix
+ * @param oid OID
+ * @param oid_len OID length
+ */
+void
+snmp_oid_prefix(struct snmp_obj_id* target, const u32_t *oid, u8_t oid_len)
+{
+  LWIP_ASSERT("target->len + oid_len <= LWIP_SNMP_OBJ_ID_LEN", (target->len + oid_len) <= SNMP_MAX_OBJ_ID_LEN);
+
+  if (oid_len > 0) {
+    /* move existing OID to make room at the beginning for OID to insert */
+    int i;
+    for (i = target->len-1; i>=0; i--) {
+      target->id[i + oid_len] = target->id[i];
+    }
+
+    /* paste oid at the beginning */
+    MEMCPY(target->id, oid, oid_len * sizeof(u32_t));
+  }
+}
+
+/**
+ * Combine two OIDs into struct snmp_obj_id
+ * @param target Assignmet target
+ * @param oid1 OID 1
+ * @param oid1_len OID 1 length
+ * @param oid2 OID 2
+ * @param oid2_len OID 2 length
+ */
+void
+snmp_oid_combine(struct snmp_obj_id* target, const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len)
+{
+  snmp_oid_assign(target, oid1, oid1_len);
+  snmp_oid_append(target, oid2, oid2_len);
+}
+
+/**
+ * Append OIDs to struct snmp_obj_id
+ * @param target Assignment target to append to
+ * @param oid OID
+ * @param oid_len OID length
+ */
+void
+snmp_oid_append(struct snmp_obj_id* target, const u32_t *oid, u8_t oid_len)
+{
+  LWIP_ASSERT("offset + oid_len <= LWIP_SNMP_OBJ_ID_LEN", (target->len + oid_len) <= SNMP_MAX_OBJ_ID_LEN);
+
+  if (oid_len > 0) {
+    MEMCPY(&target->id[target->len], oid, oid_len * sizeof(u32_t));
+    target->len += oid_len;
+  }
+}
+
+/**
+ * Compare two OIDs
+ * @param oid1 OID 1
+ * @param oid1_len OID 1 length
+ * @param oid2 OID 2
+ * @param oid2_len OID 2 length
+ * @return -1: OID1&lt;OID2  1: OID1 &gt;OID2 0: equal
+ */
+s8_t
+snmp_oid_compare(const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len)
+{
+  u8_t level = 0;
+  LWIP_ASSERT("'oid1' param must not be NULL or 'oid1_len' param be 0!", (oid1 != NULL) || (oid1_len == 0));
+  LWIP_ASSERT("'oid2' param must not be NULL or 'oid2_len' param be 0!", (oid2 != NULL) || (oid2_len == 0));
+
+  while ((level < oid1_len) && (level < oid2_len)) {
+    if (*oid1 < *oid2) {
+      return -1;
+    }
+    if (*oid1 > *oid2) {
+      return 1;
+    }
+
+    level++;
+    oid1++;
+    oid2++;
+  }
+
+  /* common part of both OID's is equal, compare length */
+  if (oid1_len < oid2_len) {
+    return -1;
+  }
+  if (oid1_len > oid2_len) {
+    return 1;
+  }
+
+  /* they are equal */
+  return 0;
+}
+
+
+/**
+ * Check of two OIDs are equal
+ * @param oid1 OID 1
+ * @param oid1_len OID 1 length
+ * @param oid2 OID 2
+ * @param oid2_len OID 2 length
+ * @return 1: equal 0: non-equal
+ */
+u8_t
+snmp_oid_equal(const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len)
+{
+  return (snmp_oid_compare(oid1, oid1_len, oid2, oid2_len) == 0)? 1 : 0;
+}
+
+/**
+ * Convert netif to interface index
+ * @param netif netif
+ * @return index
+ */
+u8_t
+netif_to_num(const struct netif *netif)
+{
+  u8_t result = 0;
+  struct netif *netif_iterator = netif_list;
+
+  while (netif_iterator != NULL) {
+    result++;
+
+    if (netif_iterator == netif) {
+      return result;
+    }
+
+    netif_iterator = netif_iterator->next;
+  }
+
+  LWIP_ASSERT("netif not found in netif_list", 0);
+  return 0;
+}
+
+static const struct snmp_mib*
+snmp_get_mib_from_oid(const u32_t *oid, u8_t oid_len)
+{
+  const u32_t* list_oid;
+  const u32_t* searched_oid;
+  u8_t i, l;
+
+  u8_t max_match_len = 0;
+  const struct snmp_mib* matched_mib = NULL;
+
+  LWIP_ASSERT("'oid' param must not be NULL!", (oid != NULL));
+
+  if (oid_len == 0) {
+    return NULL;
+  }
+
+  for (i = 0; i < snmp_num_mibs; i++) {
+    LWIP_ASSERT("MIB array not initialized correctly", (snmp_mibs[i] != NULL));
+    LWIP_ASSERT("MIB array not initialized correctly - base OID is NULL", (snmp_mibs[i]->base_oid != NULL));
+
+    if (oid_len >= snmp_mibs[i]->base_oid_len) {
+      l            = snmp_mibs[i]->base_oid_len;
+      list_oid     = snmp_mibs[i]->base_oid;
+      searched_oid = oid;
+
+      while (l > 0) {
+        if (*list_oid != *searched_oid) {
+          break;
+        }
+
+        l--;
+        list_oid++;
+        searched_oid++;
+      }
+
+      if ((l == 0) && (snmp_mibs[i]->base_oid_len > max_match_len)) {
+        max_match_len = snmp_mibs[i]->base_oid_len;
+        matched_mib = snmp_mibs[i];
+      }
+    }
+  }
+
+  return matched_mib;
+}
+
+static const struct snmp_mib*
+snmp_get_next_mib(const u32_t *oid, u8_t oid_len)
+{
+  u8_t i;
+  const struct snmp_mib* next_mib = NULL;
+
+  LWIP_ASSERT("'oid' param must not be NULL!", (oid != NULL));
+
+  if (oid_len == 0) {
+    return NULL;
+  }
+
+  for (i = 0; i < snmp_num_mibs; i++) {
+    if (snmp_mibs[i]->base_oid != NULL) {
+      /* check if mib is located behind starting point */
+      if (snmp_oid_compare(snmp_mibs[i]->base_oid, snmp_mibs[i]->base_oid_len, oid, oid_len) > 0) {
+        if ((next_mib == NULL) ||
+            (snmp_oid_compare(snmp_mibs[i]->base_oid, snmp_mibs[i]->base_oid_len,
+                              next_mib->base_oid, next_mib->base_oid_len) < 0)) {
+          next_mib = snmp_mibs[i];
+        }
+      }
+    }
+  }
+
+  return next_mib;
+}
+
+static const struct snmp_mib*
+snmp_get_mib_between(const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len)
+{
+  const struct snmp_mib* next_mib = snmp_get_next_mib(oid1, oid1_len);
+
+  LWIP_ASSERT("'oid2' param must not be NULL!", (oid2 != NULL));
+  LWIP_ASSERT("'oid2_len' param must be greater than 0!", (oid2_len > 0));
+
+  if (next_mib != NULL) {
+    if (snmp_oid_compare(next_mib->base_oid, next_mib->base_oid_len, oid2, oid2_len) < 0) {
+      return next_mib;
+    }
+  }
+
+  return NULL;
+}
+
+u8_t
+snmp_get_node_instance_from_oid(const u32_t *oid, u8_t oid_len, struct snmp_node_instance* node_instance)
+{
+  u8_t result = SNMP_ERR_NOSUCHOBJECT;
+  const struct snmp_mib *mib;
+  const struct snmp_node *mn = NULL;
+
+  mib = snmp_get_mib_from_oid(oid, oid_len);
+  if (mib != NULL) {
+    u8_t oid_instance_len;
+
+    mn = snmp_mib_tree_resolve_exact(mib, oid, oid_len, &oid_instance_len);
+    if ((mn != NULL) && (mn->node_type != SNMP_NODE_TREE)) {
+      /* get instance */
+      const struct snmp_leaf_node* leaf_node = (const struct snmp_leaf_node*)(const void*)mn;
+
+      node_instance->node = mn;
+      snmp_oid_assign(&node_instance->instance_oid, oid + (oid_len - oid_instance_len), oid_instance_len);
+
+      result = leaf_node->get_instance(
+        oid,
+        oid_len - oid_instance_len,
+        node_instance);
+
+#ifdef LWIP_DEBUG
+      if (result == SNMP_ERR_NOERROR) {
+        if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_READ) != 0) && (node_instance->get_value == NULL)) {
+          LWIP_DEBUGF(SNMP_DEBUG, ("SNMP inconsistent access: node is readable but no get_value function is specified\n"));
+        }
+        if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_WRITE) != 0) && (node_instance->set_value == NULL)) {
+          LWIP_DEBUGF(SNMP_DEBUG, ("SNMP inconsistent access: node is writable but no set_value and/or set_test function is specified\n"));
+        }
+      }
+#endif
+    }
+  }
+
+  return result;
+}
+
+u8_t
+snmp_get_next_node_instance_from_oid(const u32_t *oid, u8_t oid_len, snmp_validate_node_instance_method validate_node_instance_method, void* validate_node_instance_arg, struct snmp_obj_id* node_oid, struct snmp_node_instance* node_instance)
+{
+  const struct snmp_mib      *mib;
+  const struct snmp_node *mn = NULL;
+  const u32_t* start_oid     = NULL;
+  u8_t         start_oid_len = 0;
+
+  /* resolve target MIB from passed OID */
+  mib = snmp_get_mib_from_oid(oid, oid_len);
+  if (mib == NULL) {
+    /* passed OID does not reference any known MIB, start at the next closest MIB */
+    mib = snmp_get_next_mib(oid, oid_len);
+
+    if (mib != NULL) {
+      start_oid     = mib->base_oid;
+      start_oid_len = mib->base_oid_len;
+    }
+  } else {
+    start_oid     = oid;
+    start_oid_len = oid_len;
+  }
+
+  /* resolve target node from MIB, skip to next MIB if no suitable node is found in current MIB */
+  while ((mib != NULL) && (mn == NULL)) {
+    u8_t oid_instance_len;
+
+    /* check if OID directly references a node inside current MIB, in this case we have to ask this node for the next instance */
+    mn = snmp_mib_tree_resolve_exact(mib, start_oid, start_oid_len, &oid_instance_len);
+    if (mn != NULL) {
+      snmp_oid_assign(node_oid, start_oid, start_oid_len - oid_instance_len); /* set oid to node */
+      snmp_oid_assign(&node_instance->instance_oid, start_oid + (start_oid_len - oid_instance_len), oid_instance_len); /* set (relative) instance oid */
+    } else {
+      /* OID does not reference a node, search for the next closest node inside MIB; set instance_oid.len to zero because we want the first instance of this node */
+      mn = snmp_mib_tree_resolve_next(mib, start_oid, start_oid_len, node_oid);
+      node_instance->instance_oid.len = 0;
+    }
+
+    /* validate the node; if the node has no further instance or the returned instance is invalid, search for the next in MIB and validate again */
+    node_instance->node = mn;
+    while (mn != NULL) {
+       u8_t result;
+
+      /* clear fields which may have values from previous loops */
+      node_instance->asn1_type        = 0;
+      node_instance->access           = SNMP_NODE_INSTANCE_NOT_ACCESSIBLE;
+      node_instance->get_value        = NULL;
+      node_instance->set_test         = NULL;
+      node_instance->set_value        = NULL;
+      node_instance->release_instance = NULL;
+      node_instance->reference.ptr    = NULL;
+      node_instance->reference_len    = 0;
+
+      result = ((const struct snmp_leaf_node*)(const void*)mn)->get_next_instance(
+        node_oid->id,
+        node_oid->len,
+        node_instance);
+
+      if (result == SNMP_ERR_NOERROR) {
+#ifdef LWIP_DEBUG
+        if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_READ) != 0) && (node_instance->get_value == NULL)) {
+          LWIP_DEBUGF(SNMP_DEBUG, ("SNMP inconsistent access: node is readable but no get_value function is specified\n"));
+        }
+        if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_WRITE) != 0) && (node_instance->set_value == NULL)) {
+          LWIP_DEBUGF(SNMP_DEBUG, ("SNMP inconsistent access: node is writable but no set_value function is specified\n"));
+        }
+#endif
+
+        /* validate node because the node may be not accessible for example (but let the caller decide what is valid */
+        if ((validate_node_instance_method == NULL) ||
+            (validate_node_instance_method(node_instance, validate_node_instance_arg) == SNMP_ERR_NOERROR)) {
+          /* node_oid "returns" the full result OID (including the instance part) */
+          snmp_oid_append(node_oid, node_instance->instance_oid.id, node_instance->instance_oid.len);
+          break;
+        }
+
+        if (node_instance->release_instance != NULL) {
+          node_instance->release_instance(node_instance);
+        }
+        /*
+        the instance itself is not valid, ask for next instance from same node.
+        we don't have to change any variables because node_instance->instance_oid is used as input (starting point)
+        as well as output (resulting next OID), so we have to simply call get_next_instance method again
+        */
+      } else {
+        if (node_instance->release_instance != NULL) {
+          node_instance->release_instance(node_instance);
+        }
+
+        /* the node has no further instance, skip to next node */
+        mn = snmp_mib_tree_resolve_next(mib, node_oid->id, node_oid->len, &node_instance->instance_oid); /* misuse node_instance->instance_oid as tmp buffer */
+        if (mn != NULL) {
+          /* prepare for next loop */
+          snmp_oid_assign(node_oid, node_instance->instance_oid.id, node_instance->instance_oid.len);
+          node_instance->instance_oid.len = 0;
+          node_instance->node = mn;
+        }
+      }
+    }
+
+    if (mn != NULL) {
+      /*
+      we found a suitable next node,
+      now we have to check if a inner MIB is located between the searched OID and the resulting OID.
+      this is possible because MIB's may be located anywhere in the global tree, that means also in 
+      the subtree of another MIB (e.g. if searched OID is .2 and resulting OID is .4, then another
+      MIB having .3 as root node may exist)
+      */
+      const struct snmp_mib *intermediate_mib;
+      intermediate_mib = snmp_get_mib_between(start_oid, start_oid_len, node_oid->id, node_oid->len);
+
+      if (intermediate_mib != NULL) {
+        /* search for first node inside intermediate mib in next loop */
+        if (node_instance->release_instance != NULL) {
+          node_instance->release_instance(node_instance);
+        }
+
+        mn            = NULL;
+        mib           = intermediate_mib;
+        start_oid     = mib->base_oid;
+        start_oid_len = mib->base_oid_len;
+      }
+      /* else { we found out target node } */
+    } else {
+      /*
+      there is no further (suitable) node inside this MIB, search for the next MIB with following priority
+      1. search for inner MIB's (whose root is located inside tree of current MIB)
+      2. search for surrouding MIB's (where the current MIB is the inner MIB) and continue there if any
+      3. take the next closest MIB (not being related to the current MIB)
+      */
+      const struct snmp_mib *next_mib;
+      next_mib = snmp_get_next_mib(start_oid, start_oid_len); /* returns MIB's related to point 1 and 3 */
+
+      /* is the found MIB an inner MIB? (point 1) */
+      if ((next_mib != NULL) && (next_mib->base_oid_len > mib->base_oid_len) &&
+          (snmp_oid_compare(next_mib->base_oid, mib->base_oid_len, mib->base_oid, mib->base_oid_len) == 0)) {
+        /* yes it is -> continue at inner MIB */
+        mib = next_mib;
+        start_oid     = mib->base_oid;
+        start_oid_len = mib->base_oid_len;
+      } else {
+        /* check if there is a surrounding mib where to continue (point 2) (only possible if OID length > 1) */
+        if (mib->base_oid_len > 1) {
+          mib = snmp_get_mib_from_oid(mib->base_oid, mib->base_oid_len - 1);
+
+          if (mib == NULL) {
+            /* no surrounding mib, use next mib encountered above (point 3) */
+            mib = next_mib;
+
+            if (mib != NULL) {
+              start_oid     = mib->base_oid;
+              start_oid_len = mib->base_oid_len;
+            }
+          }
+          /* else { start_oid stays the same because we want to continue from current offset in surrounding mib (point 2) } */
+        }
+      }
+    }
+  }
+
+  if (mib == NULL) {
+    /* loop is only left when mib == null (error) or mib_node != NULL (success) */
+    return SNMP_ERR_ENDOFMIBVIEW;
+  }
+
+  return SNMP_ERR_NOERROR;
+}
+
+/**
+ * Searches tree for the supplied object identifier.
+ *
+ */
+const struct snmp_node *
+snmp_mib_tree_resolve_exact(const struct snmp_mib *mib, const u32_t *oid, u8_t oid_len, u8_t* oid_instance_len)
+{
+  const struct snmp_node* const* node = &mib->root_node;
+  u8_t oid_offset = mib->base_oid_len;
+
+  while ((oid_offset < oid_len) && ((*node)->node_type == SNMP_NODE_TREE)) {
+    /* search for matching sub node */
+    u32_t subnode_oid = *(oid + oid_offset);
+
+    u32_t i = (*(const struct snmp_tree_node* const*)node)->subnode_count;
+    node    = (*(const struct snmp_tree_node* const*)node)->subnodes;
+    while ((i > 0) && ((*node)->oid != subnode_oid)) {
+      node++;
+      i--;
+    }
+
+    if (i == 0) {
+      /* no matching subnode found */
+      return NULL;
+    }
+
+    oid_offset++;
+  }
+
+  if ((*node)->node_type != SNMP_NODE_TREE) {
+    /* we found a leaf node */
+    *oid_instance_len = oid_len - oid_offset;
+    return (*node);
+  }
+
+  return NULL;
+}
+
+const struct snmp_node*
+snmp_mib_tree_resolve_next(const struct snmp_mib *mib, const u32_t *oid, u8_t oid_len, struct snmp_obj_id* oidret)
+{
+  u8_t  oid_offset = mib->base_oid_len;
+  const struct snmp_node* const* node;
+  const struct snmp_tree_node* node_stack[SNMP_MAX_OBJ_ID_LEN];
+  s32_t nsi = 0; /* NodeStackIndex */
+  u32_t subnode_oid;
+
+  if (mib->root_node->node_type != SNMP_NODE_TREE) {
+    /* a next operation on a mib with only a leaf node will always return NULL because there is no other node */
+    return NULL;
+  }
+
+  /* first build node stack related to passed oid (as far as possible), then go backwards to determine the next node */
+  node_stack[nsi] = (const struct snmp_tree_node*)(const void*)mib->root_node;
+  while (oid_offset < oid_len) {
+    /* search for matching sub node */
+    u32_t i = node_stack[nsi]->subnode_count;
+    node    = node_stack[nsi]->subnodes;
+
+    subnode_oid = *(oid + oid_offset);
+
+    while ((i > 0) && ((*node)->oid != subnode_oid)) {
+      node++;
+      i--;
+    }
+
+    if ((i == 0) || ((*node)->node_type != SNMP_NODE_TREE)) {
+      /* no (matching) tree-subnode found */
+      break;
+    }
+    nsi++;
+    node_stack[nsi] = (const struct snmp_tree_node*)(const void*)(*node);
+
+    oid_offset++;
+  }
+
+
+  if (oid_offset >= oid_len) {
+    /* passed oid references a tree node -> return first useable sub node of it */
+    subnode_oid = 0;
+  } else {
+    subnode_oid = *(oid + oid_offset) + 1;
+  }
+
+  while (nsi >= 0) {
+    const struct snmp_node* subnode = NULL;
+
+    /* find next node on current level */
+    s32_t i        = node_stack[nsi]->subnode_count;
+    node           = node_stack[nsi]->subnodes;
+    while (i > 0) {
+      if ((*node)->oid == subnode_oid) {
+        subnode = *node;
+        break;
+      } else if (((*node)->oid > subnode_oid) && ((subnode == NULL) || ((*node)->oid < subnode->oid))) {
+        subnode = *node;
+      }
+
+      node++;
+      i--;
+    }
+
+    if (subnode == NULL) {
+      /* no further node found on this level, go one level up and start searching with index of current node*/
+      subnode_oid = node_stack[nsi]->node.oid + 1;
+      nsi--;
+    } else {
+      if (subnode->node_type == SNMP_NODE_TREE) {
+        /* next is a tree node, go into it and start searching */
+        nsi++;
+        node_stack[nsi] = (const struct snmp_tree_node*)(const void*)subnode;
+        subnode_oid = 0;
+      } else {
+        /* we found a leaf node -> fill oidret and return it */
+        snmp_oid_assign(oidret, mib->base_oid, mib->base_oid_len);
+        i = 1;
+        while (i <= nsi) {
+          oidret->id[oidret->len] = node_stack[i]->node.oid;
+          oidret->len++;
+          i++;
+        }
+
+        oidret->id[oidret->len] = subnode->oid;
+        oidret->len++;
+
+        return subnode;
+      }
+    }
+  }
+
+  return NULL;
+}
+
+/** initialize struct next_oid_state using this function before passing it to next_oid_check */
+void
+snmp_next_oid_init(struct snmp_next_oid_state *state,
+  const u32_t *start_oid, u8_t start_oid_len,
+  u32_t *next_oid_buf, u8_t next_oid_max_len)
+{
+  state->start_oid        = start_oid;
+  state->start_oid_len    = start_oid_len;
+  state->next_oid         = next_oid_buf;
+  state->next_oid_len     = 0;
+  state->next_oid_max_len = next_oid_max_len;
+  state->status           = SNMP_NEXT_OID_STATUS_NO_MATCH;
+}
+
+/** checks if the passed incomplete OID may be a possible candidate for snmp_next_oid_check();
+this methid is intended if the complete OID is not yet known but it is very expensive to build it up,
+so it is possible to test the starting part before building up the complete oid and pass it to snmp_next_oid_check()*/
+u8_t
+snmp_next_oid_precheck(struct snmp_next_oid_state *state, const u32_t *oid, const u8_t oid_len)
+{
+  if (state->status != SNMP_NEXT_OID_STATUS_BUF_TO_SMALL) {
+    u8_t start_oid_len = (oid_len < state->start_oid_len) ? oid_len : state->start_oid_len;
+
+    /* check passed OID is located behind start offset */
+    if (snmp_oid_compare(oid, oid_len, state->start_oid, start_oid_len) >= 0) {
+      /* check if new oid is located closer to start oid than current closest oid */
+      if ((state->status == SNMP_NEXT_OID_STATUS_NO_MATCH) ||
+        (snmp_oid_compare(oid, oid_len, state->next_oid, state->next_oid_len) < 0)) {
+        return 1;
+      }
+    }
+  }
+
+  return 0;
+}
+
+/** checks the passed OID if it is a candidate to be the next one (get_next); returns !=0 if passed oid is currently closest, otherwise 0 */
+u8_t
+snmp_next_oid_check(struct snmp_next_oid_state *state, const u32_t *oid, const u8_t oid_len, void* reference)
+{
+  /* do not overwrite a fail result */
+  if (state->status != SNMP_NEXT_OID_STATUS_BUF_TO_SMALL) {
+    /* check passed OID is located behind start offset */
+    if (snmp_oid_compare(oid, oid_len, state->start_oid, state->start_oid_len) > 0) {
+      /* check if new oid is located closer to start oid than current closest oid */
+      if ((state->status == SNMP_NEXT_OID_STATUS_NO_MATCH) ||
+        (snmp_oid_compare(oid, oid_len, state->next_oid, state->next_oid_len) < 0)) {
+        if (oid_len <= state->next_oid_max_len) {
+          MEMCPY(state->next_oid, oid, oid_len * sizeof(u32_t));
+          state->next_oid_len = oid_len;
+          state->status       = SNMP_NEXT_OID_STATUS_SUCCESS;
+          state->reference    = reference;
+          return 1;
+        } else {
+          state->status = SNMP_NEXT_OID_STATUS_BUF_TO_SMALL;
+        }
+      }
+    }
+  }
+
+  return 0;
+}
+
+u8_t
+snmp_oid_in_range(const u32_t *oid_in, u8_t oid_len, const struct snmp_oid_range *oid_ranges, u8_t oid_ranges_len)
+{
+  u8_t i;
+
+  if (oid_len != oid_ranges_len) {
+    return 0;
+  }
+
+  for (i = 0; i < oid_ranges_len; i++) {
+    if ((oid_in[i] < oid_ranges[i].min) || (oid_in[i] > oid_ranges[i].max)) {
+      return 0;
+    }
+  }
+
+  return 1;
+}
+
+snmp_err_t
+snmp_set_test_ok(struct snmp_node_instance* instance, u16_t value_len, void* value)
+{
+  LWIP_UNUSED_ARG(instance);
+  LWIP_UNUSED_ARG(value_len);
+  LWIP_UNUSED_ARG(value);
+
+  return SNMP_ERR_NOERROR;
+}
+
+/**
+ * Decodes BITS pseudotype value from ASN.1 OctetString.
+ *
+ * @note Because BITS pseudo type is encoded as OCTET STRING, it cannot directly
+ * be encoded/decoded by the agent. Instead call this function as required from
+ * get/test/set methods.
+ *
+ * @param buf points to a buffer holding the ASN1 octet string
+ * @param buf_len length of octet string
+ * @param bit_value decoded Bit value with Bit0 == LSB
+ * @return ERR_OK if successful, ERR_ARG if bit value contains more than 32 bit
+ */
+err_t
+snmp_decode_bits(const u8_t *buf, u32_t buf_len, u32_t *bit_value)
+{
+  u8_t b;
+  u8_t bits_processed = 0;
+  *bit_value = 0;
+
+  while (buf_len > 0) {
+    /* any bit set in this byte? */
+    if (*buf != 0x00) {
+      if (bits_processed >= 32) {
+        /* accept more than 4 bytes, but only when no bits are set */
+        return ERR_VAL;
+      }
+
+      b = *buf;
+      do {
+        if (b & 0x80) {
+          *bit_value |= (1 << bits_processed);
+        }
+        bits_processed++;
+        b <<= 1;
+      }
+      while ((bits_processed & 0x07) != 0); /* &0x07 -> % 8 */
+    } else {
+      bits_processed += 8;
+    }
+
+    buf_len--;
+    buf++;
+  }
+
+  return ERR_OK;
+}
+
+err_t
+snmp_decode_truthvalue(const s32_t *asn1_value, u8_t *bool_value)
+{
+  /* defined by RFC1443:
+   TruthValue ::= TEXTUAL-CONVENTION
+    STATUS       current
+    DESCRIPTION
+     "Represents a boolean value."
+    SYNTAX       INTEGER { true(1), false(2) }
+  */
+
+  if ((asn1_value == NULL) || (bool_value == NULL)) {
+    return ERR_ARG;
+  }
+
+  if (*asn1_value == 1) {
+    *bool_value = 1;
+  } else if (*asn1_value == 2) {
+    *bool_value = 0;
+  } else {
+    return ERR_VAL;
+  }
+
+  return ERR_OK;
+}
+
+/**
+ * Encodes BITS pseudotype value into ASN.1 OctetString.
+ *
+ * @note Because BITS pseudo type is encoded as OCTET STRING, it cannot directly
+ * be encoded/decoded by the agent. Instead call this function as required from
+ * get/test/set methods.
+ *
+ * @param buf points to a buffer where the resulting ASN1 octet string is stored to
+ * @param buf_len max length of the bufffer
+ * @param bit_value Bit value to encode with Bit0 == LSB
+ * @param bit_count Number of possible bits for the bit value (according to rfc we have to send all bits independant from their truth value)
+ * @return number of bytes used from buffer to store the resulting OctetString
+ */
+u8_t
+snmp_encode_bits(u8_t *buf, u32_t buf_len, u32_t bit_value, u8_t bit_count)
+{
+  u8_t len = 0;
+  u8_t min_bytes = (bit_count + 7) >> 3; /* >>3 -> / 8 */
+
+  while ((buf_len > 0) && (bit_value != 0x00)) {
+    s8_t i = 7;
+    *buf = 0x00;
+    while (i >= 0) {
+      if (bit_value & 0x01) {
+        *buf |= 0x01;
+      }
+
+      if (i > 0) {
+        *buf <<= 1;
+      }
+
+      bit_value >>= 1;
+      i--;
+    }
+
+    buf++;
+    buf_len--;
+    len++;
+  }
+
+  if (len < min_bytes) {
+    buf     += len;
+    buf_len -= len;
+
+    while ((len < min_bytes) && (buf_len > 0)) {
+      *buf = 0x00;
+      buf++;
+      buf_len--;
+      len++;
+    }
+  }
+
+  return len;
+}
+
+u8_t
+snmp_encode_truthvalue(s32_t *asn1_value, u32_t bool_value)
+{
+  /* defined by RFC1443:
+   TruthValue ::= TEXTUAL-CONVENTION
+    STATUS       current
+    DESCRIPTION
+     "Represents a boolean value."
+    SYNTAX       INTEGER { true(1), false(2) }
+  */
+
+  if (asn1_value == NULL) {
+    return 0;
+  }
+
+  if (bool_value) {
+    *asn1_value = 1; /* defined by RFC1443 */
+  } else {
+    *asn1_value = 2; /* defined by RFC1443 */
+  }
+
+  return sizeof(s32_t);
+}
+
+#endif /* LWIP_SNMP */

+ 76 - 83
libs/thirdparty/lwip_2.1.2/src/apps/snmp/snmp_core_priv.h → libs/thirdparty/LwIP/src/apps/snmp/snmp_core_priv.h

@@ -1,83 +1,76 @@
-/*
- * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- *
- * Author: Martin Hentschel <info@cl-soft.de>
- *
- */
-
-#ifndef LWIP_HDR_APPS_SNMP_CORE_PRIV_H
-#define LWIP_HDR_APPS_SNMP_CORE_PRIV_H
-
-#include "lwip/apps/snmp_opts.h"
-
-#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
-
-#include "lwip/apps/snmp_core.h"
-#include "snmp_asn1.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* (outdated) SNMPv1 error codes
- * shall not be used by MIBS anymore, nevertheless required from core for properly answering a v1 request
- */
-#define SNMP_ERR_NOSUCHNAME 2
-#define SNMP_ERR_BADVALUE   3
-#define SNMP_ERR_READONLY   4
-/* error codes which are internal and shall not be used by MIBS
- * shall not be used by MIBS anymore, nevertheless required from core for properly answering a v1 request
- */
-#define SNMP_ERR_TOOBIG               1
-#define SNMP_ERR_AUTHORIZATIONERROR   16
-
-#define SNMP_ERR_UNKNOWN_ENGINEID     30
-#define SNMP_ERR_UNKNOWN_SECURITYNAME 31
-#define SNMP_ERR_UNSUPPORTED_SECLEVEL 32
-#define SNMP_ERR_NOTINTIMEWINDOW      33
-#define SNMP_ERR_DECRYIPTION_ERROR    34
-
-#define SNMP_ERR_NOSUCHOBJECT         SNMP_VARBIND_EXCEPTION_OFFSET + SNMP_ASN1_CONTEXT_VARBIND_NO_SUCH_OBJECT
-#define SNMP_ERR_ENDOFMIBVIEW         SNMP_VARBIND_EXCEPTION_OFFSET + SNMP_ASN1_CONTEXT_VARBIND_END_OF_MIB_VIEW
-
-
-const struct snmp_node *snmp_mib_tree_resolve_exact(const struct snmp_mib *mib, const u32_t *oid, u8_t oid_len, u8_t *oid_instance_len);
-const struct snmp_node *snmp_mib_tree_resolve_next(const struct snmp_mib *mib, const u32_t *oid, u8_t oid_len, struct snmp_obj_id *oidret);
-
-typedef u8_t (*snmp_validate_node_instance_method)(struct snmp_node_instance *, void *);
-
-u8_t snmp_get_node_instance_from_oid(const u32_t *oid, u8_t oid_len, struct snmp_node_instance *node_instance);
-u8_t snmp_get_next_node_instance_from_oid(const u32_t *oid, u8_t oid_len, snmp_validate_node_instance_method validate_node_instance_method, void *validate_node_instance_arg, struct snmp_obj_id *node_oid, struct snmp_node_instance *node_instance);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* LWIP_SNMP */
-
-#endif /* LWIP_HDR_APPS_SNMP_CORE_PRIV_H */
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Martin Hentschel <info@cl-soft.de>
+ *
+ */
+
+#ifndef LWIP_HDR_APPS_SNMP_CORE_PRIV_H
+#define LWIP_HDR_APPS_SNMP_CORE_PRIV_H
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/apps/snmp_core.h"
+#include "snmp_asn1.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* (outdated) SNMPv1 error codes
+ * shall not be used by MIBS anymore, nevertheless required from core for properly answering a v1 request
+ */
+#define SNMP_ERR_NOSUCHNAME 2
+#define SNMP_ERR_BADVALUE   3
+#define SNMP_ERR_READONLY   4
+/* error codes which are internal and shall not be used by MIBS
+ * shall not be used by MIBS anymore, nevertheless required from core for properly answering a v1 request
+ */
+#define SNMP_ERR_TOOBIG               1
+#define SNMP_ERR_AUTHORIZATIONERROR   16
+#define SNMP_ERR_NOSUCHOBJECT         SNMP_VARBIND_EXCEPTION_OFFSET + SNMP_ASN1_CONTEXT_VARBIND_NO_SUCH_OBJECT
+#define SNMP_ERR_ENDOFMIBVIEW         SNMP_VARBIND_EXCEPTION_OFFSET + SNMP_ASN1_CONTEXT_VARBIND_END_OF_MIB_VIEW
+
+
+const struct snmp_node* snmp_mib_tree_resolve_exact(const struct snmp_mib *mib, const u32_t *oid, u8_t oid_len, u8_t* oid_instance_len);
+const struct snmp_node* snmp_mib_tree_resolve_next(const struct snmp_mib *mib, const u32_t *oid, u8_t oid_len, struct snmp_obj_id* oidret);
+
+typedef u8_t (*snmp_validate_node_instance_method)(struct snmp_node_instance*, void*);
+
+u8_t snmp_get_node_instance_from_oid(const u32_t *oid, u8_t oid_len, struct snmp_node_instance* node_instance);
+u8_t snmp_get_next_node_instance_from_oid(const u32_t *oid, u8_t oid_len, snmp_validate_node_instance_method validate_node_instance_method, void* validate_node_instance_arg, struct snmp_obj_id* node_oid, struct snmp_node_instance* node_instance);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_SNMP */
+
+#endif /* LWIP_HDR_APPS_SNMP_CORE_PRIV_H */

+ 116 - 116
libs/thirdparty/lwip_2.1.2/src/apps/snmp/snmp_mib2.c → libs/thirdparty/LwIP/src/apps/snmp/snmp_mib2.c

@@ -1,116 +1,116 @@
-/**
- * @file
- * Management Information Base II (RFC1213) objects and functions.
- */
-
-/*
- * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * Author: Dirk Ziegelmeier <dziegel@gmx.de>
- *         Christiaan Simons <christiaan.simons@axon.tv>
- */
-
-/**
- * @defgroup snmp_mib2 MIB2
- * @ingroup snmp
- */
-
-#include "lwip/apps/snmp_opts.h"
-
-#if LWIP_SNMP && SNMP_LWIP_MIB2 /* don't build if not configured for use in lwipopts.h */
-
-#if !LWIP_STATS
-#error LWIP_SNMP MIB2 needs LWIP_STATS (for MIB2)
-#endif
-#if !MIB2_STATS
-#error LWIP_SNMP MIB2 needs MIB2_STATS (for MIB2)
-#endif
-
-#include "lwip/snmp.h"
-#include "lwip/apps/snmp.h"
-#include "lwip/apps/snmp_core.h"
-#include "lwip/apps/snmp_mib2.h"
-#include "lwip/apps/snmp_scalar.h"
-
-#if SNMP_USE_NETCONN
-#include "lwip/tcpip.h"
-#include "lwip/priv/tcpip_priv.h"
-void
-snmp_mib2_lwip_synchronizer(snmp_threadsync_called_fn fn, void *arg)
-{
-#if LWIP_TCPIP_CORE_LOCKING
-  LOCK_TCPIP_CORE();
-  fn(arg);
-  UNLOCK_TCPIP_CORE();
-#else
-  tcpip_callback(fn, arg);
-#endif
-}
-
-struct snmp_threadsync_instance snmp_mib2_lwip_locks;
-#endif
-
-/* dot3 and EtherLike MIB not planned. (transmission .1.3.6.1.2.1.10) */
-/* historical (some say hysterical). (cmot .1.3.6.1.2.1.9) */
-/* lwIP has no EGP, thus may not implement it. (egp .1.3.6.1.2.1.8) */
-
-/* --- mib-2 .1.3.6.1.2.1 ----------------------------------------------------- */
-extern const struct snmp_scalar_array_node snmp_mib2_snmp_root;
-extern const struct snmp_tree_node snmp_mib2_udp_root;
-extern const struct snmp_tree_node snmp_mib2_tcp_root;
-extern const struct snmp_scalar_array_node snmp_mib2_icmp_root;
-extern const struct snmp_tree_node snmp_mib2_interface_root;
-extern const struct snmp_scalar_array_node snmp_mib2_system_node;
-extern const struct snmp_tree_node snmp_mib2_at_root;
-extern const struct snmp_tree_node snmp_mib2_ip_root;
-
-static const struct snmp_node *const mib2_nodes[] = {
-  &snmp_mib2_system_node.node.node,
-  &snmp_mib2_interface_root.node,
-#if LWIP_ARP && LWIP_IPV4
-  &snmp_mib2_at_root.node,
-#endif /* LWIP_ARP && LWIP_IPV4 */
-#if LWIP_IPV4
-  &snmp_mib2_ip_root.node,
-#endif /* LWIP_IPV4 */
-#if LWIP_ICMP
-  &snmp_mib2_icmp_root.node.node,
-#endif /* LWIP_ICMP */
-#if LWIP_TCP
-  &snmp_mib2_tcp_root.node,
-#endif /* LWIP_TCP */
-#if LWIP_UDP
-  &snmp_mib2_udp_root.node,
-#endif /* LWIP_UDP */
-  &snmp_mib2_snmp_root.node.node
-};
-
-static const struct snmp_tree_node mib2_root = SNMP_CREATE_TREE_NODE(1, mib2_nodes);
-
-static const u32_t  mib2_base_oid_arr[] = { 1, 3, 6, 1, 2, 1 };
-const struct snmp_mib mib2 = SNMP_MIB_CREATE(mib2_base_oid_arr, &mib2_root.node);
-
-#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 */
+/**
+ * @file
+ * Management Information Base II (RFC1213) objects and functions.
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Dirk Ziegelmeier <dziegel@gmx.de>
+ *         Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+/**
+ * @defgroup snmp_mib2 MIB2
+ * @ingroup snmp
+ */
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP && SNMP_LWIP_MIB2 /* don't build if not configured for use in lwipopts.h */
+
+#if !LWIP_STATS
+#error LWIP_SNMP MIB2 needs LWIP_STATS (for MIB2)
+#endif
+#if !MIB2_STATS
+#error LWIP_SNMP MIB2 needs MIB2_STATS (for MIB2)
+#endif
+
+#include "lwip/snmp.h"
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "lwip/apps/snmp_mib2.h"
+#include "lwip/apps/snmp_scalar.h"
+
+#if SNMP_USE_NETCONN
+#include "lwip/tcpip.h"
+#include "lwip/priv/tcpip_priv.h"
+void
+snmp_mib2_lwip_synchronizer(snmp_threadsync_called_fn fn, void* arg)
+{
+#if LWIP_TCPIP_CORE_LOCKING
+  LOCK_TCPIP_CORE();
+  fn(arg);
+  UNLOCK_TCPIP_CORE();
+#else
+  tcpip_callback(fn, arg);
+#endif
+}
+
+struct snmp_threadsync_instance snmp_mib2_lwip_locks;
+#endif
+
+/* dot3 and EtherLike MIB not planned. (transmission .1.3.6.1.2.1.10) */
+/* historical (some say hysterical). (cmot .1.3.6.1.2.1.9) */
+/* lwIP has no EGP, thus may not implement it. (egp .1.3.6.1.2.1.8) */
+
+/* --- mib-2 .1.3.6.1.2.1 ----------------------------------------------------- */
+extern const struct snmp_scalar_array_node snmp_mib2_snmp_root;
+extern const struct snmp_tree_node snmp_mib2_udp_root;
+extern const struct snmp_tree_node snmp_mib2_tcp_root;
+extern const struct snmp_scalar_array_node snmp_mib2_icmp_root;
+extern const struct snmp_tree_node snmp_mib2_interface_root;
+extern const struct snmp_scalar_array_node snmp_mib2_system_node;
+extern const struct snmp_tree_node snmp_mib2_at_root;
+extern const struct snmp_tree_node snmp_mib2_ip_root;
+
+static const struct snmp_node* const mib2_nodes[] = {
+  &snmp_mib2_system_node.node.node,
+  &snmp_mib2_interface_root.node,
+#if LWIP_ARP && LWIP_IPV4
+  //&snmp_mib2_at_root.node,
+#endif /* LWIP_ARP && LWIP_IPV4 */
+#if LWIP_IPV4
+  //&snmp_mib2_ip_root.node,
+#endif /* LWIP_IPV4 */
+#if LWIP_ICMP
+  //&snmp_mib2_icmp_root.node.node,
+#endif /* LWIP_ICMP */
+#if LWIP_TCP
+  //&snmp_mib2_tcp_root.node,
+#endif /* LWIP_TCP */
+#if LWIP_UDP
+  //&snmp_mib2_udp_root.node,
+#endif /* LWIP_UDP */
+  //&snmp_mib2_snmp_root.node.node
+};
+
+static const struct snmp_tree_node mib2_root = SNMP_CREATE_TREE_NODE(1, mib2_nodes);
+
+static const u32_t  mib2_base_oid_arr[] = { 1,3,6,1,2,1 };
+const struct snmp_mib mib2 = SNMP_MIB_CREATE(mib2_base_oid_arr, &mib2_root.node);
+
+#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 */

+ 182 - 182
libs/thirdparty/lwip_2.1.2/src/apps/snmp/snmp_mib2_icmp.c → libs/thirdparty/LwIP/src/apps/snmp/snmp_mib2_icmp.c

@@ -1,182 +1,182 @@
-/**
- * @file
- * Management Information Base II (RFC1213) ICMP objects and functions.
- */
-
-/*
- * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * Author: Dirk Ziegelmeier <dziegel@gmx.de>
- *         Christiaan Simons <christiaan.simons@axon.tv>
- */
-
-#include "lwip/snmp.h"
-#include "lwip/apps/snmp.h"
-#include "lwip/apps/snmp_core.h"
-#include "lwip/apps/snmp_mib2.h"
-#include "lwip/apps/snmp_table.h"
-#include "lwip/apps/snmp_scalar.h"
-#include "lwip/icmp.h"
-#include "lwip/stats.h"
-
-#if LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_ICMP
-
-#if SNMP_USE_NETCONN
-#define SYNC_NODE_NAME(node_name) node_name ## _synced
-#define CREATE_LWIP_SYNC_NODE(oid, node_name) \
-   static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks);
-#else
-#define SYNC_NODE_NAME(node_name) node_name
-#define CREATE_LWIP_SYNC_NODE(oid, node_name)
-#endif
-
-/* --- icmp .1.3.6.1.2.1.5 ----------------------------------------------------- */
-
-static s16_t
-icmp_get_value(const struct snmp_scalar_array_node_def *node, void *value)
-{
-  u32_t *uint_ptr = (u32_t *)value;
-
-  switch (node->oid) {
-    case 1: /* icmpInMsgs */
-      *uint_ptr = STATS_GET(mib2.icmpinmsgs);
-      return sizeof(*uint_ptr);
-    case 2: /* icmpInErrors */
-      *uint_ptr = STATS_GET(mib2.icmpinerrors);
-      return sizeof(*uint_ptr);
-    case 3: /* icmpInDestUnreachs */
-      *uint_ptr = STATS_GET(mib2.icmpindestunreachs);
-      return sizeof(*uint_ptr);
-    case 4: /* icmpInTimeExcds */
-      *uint_ptr = STATS_GET(mib2.icmpintimeexcds);
-      return sizeof(*uint_ptr);
-    case 5: /* icmpInParmProbs */
-      *uint_ptr = STATS_GET(mib2.icmpinparmprobs);
-      return sizeof(*uint_ptr);
-    case 6: /* icmpInSrcQuenchs */
-      *uint_ptr = STATS_GET(mib2.icmpinsrcquenchs);
-      return sizeof(*uint_ptr);
-    case 7: /* icmpInRedirects */
-      *uint_ptr = STATS_GET(mib2.icmpinredirects);
-      return sizeof(*uint_ptr);
-    case 8: /* icmpInEchos */
-      *uint_ptr = STATS_GET(mib2.icmpinechos);
-      return sizeof(*uint_ptr);
-    case 9: /* icmpInEchoReps */
-      *uint_ptr = STATS_GET(mib2.icmpinechoreps);
-      return sizeof(*uint_ptr);
-    case 10: /* icmpInTimestamps */
-      *uint_ptr = STATS_GET(mib2.icmpintimestamps);
-      return sizeof(*uint_ptr);
-    case 11: /* icmpInTimestampReps */
-      *uint_ptr = STATS_GET(mib2.icmpintimestampreps);
-      return sizeof(*uint_ptr);
-    case 12: /* icmpInAddrMasks */
-      *uint_ptr = STATS_GET(mib2.icmpinaddrmasks);
-      return sizeof(*uint_ptr);
-    case 13: /* icmpInAddrMaskReps */
-      *uint_ptr = STATS_GET(mib2.icmpinaddrmaskreps);
-      return sizeof(*uint_ptr);
-    case 14: /* icmpOutMsgs */
-      *uint_ptr = STATS_GET(mib2.icmpoutmsgs);
-      return sizeof(*uint_ptr);
-    case 15: /* icmpOutErrors */
-      *uint_ptr = STATS_GET(mib2.icmpouterrors);
-      return sizeof(*uint_ptr);
-    case 16: /* icmpOutDestUnreachs */
-      *uint_ptr = STATS_GET(mib2.icmpoutdestunreachs);
-      return sizeof(*uint_ptr);
-    case 17: /* icmpOutTimeExcds */
-      *uint_ptr = STATS_GET(mib2.icmpouttimeexcds);
-      return sizeof(*uint_ptr);
-    case 18: /* icmpOutParmProbs: not supported -> always 0 */
-      *uint_ptr = 0;
-      return sizeof(*uint_ptr);
-    case 19: /* icmpOutSrcQuenchs: not supported -> always 0 */
-      *uint_ptr = 0;
-      return sizeof(*uint_ptr);
-    case 20: /* icmpOutRedirects: not supported -> always 0 */
-      *uint_ptr = 0;
-      return sizeof(*uint_ptr);
-    case 21: /* icmpOutEchos */
-      *uint_ptr = STATS_GET(mib2.icmpoutechos);
-      return sizeof(*uint_ptr);
-    case 22: /* icmpOutEchoReps */
-      *uint_ptr = STATS_GET(mib2.icmpoutechoreps);
-      return sizeof(*uint_ptr);
-    case 23: /* icmpOutTimestamps: not supported -> always 0 */
-      *uint_ptr = 0;
-      return sizeof(*uint_ptr);
-    case 24: /* icmpOutTimestampReps: not supported -> always 0 */
-      *uint_ptr = 0;
-      return sizeof(*uint_ptr);
-    case 25: /* icmpOutAddrMasks: not supported -> always 0 */
-      *uint_ptr = 0;
-      return sizeof(*uint_ptr);
-    case 26: /* icmpOutAddrMaskReps: not supported -> always 0 */
-      *uint_ptr = 0;
-      return sizeof(*uint_ptr);
-    default:
-      LWIP_DEBUGF(SNMP_MIB_DEBUG, ("icmp_get_value(): unknown id: %"S32_F"\n", node->oid));
-      break;
-  }
-
-  return 0;
-}
-
-
-static const struct snmp_scalar_array_node_def icmp_nodes[] = {
-  { 1, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
-  { 2, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
-  { 3, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
-  { 4, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
-  { 5, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
-  { 6, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
-  { 7, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
-  { 8, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
-  { 9, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
-  {10, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
-  {11, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
-  {12, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
-  {13, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
-  {14, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
-  {15, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
-  {16, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
-  {17, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
-  {18, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
-  {19, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
-  {20, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
-  {21, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
-  {22, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
-  {23, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
-  {24, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
-  {25, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
-  {26, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}
-};
-
-const struct snmp_scalar_array_node snmp_mib2_icmp_root = SNMP_SCALAR_CREATE_ARRAY_NODE(5, icmp_nodes, icmp_get_value, NULL, NULL);
-
-#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_ICMP */
+/**
+ * @file
+ * Management Information Base II (RFC1213) ICMP objects and functions.
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Dirk Ziegelmeier <dziegel@gmx.de>
+ *         Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#include "lwip/snmp.h"
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "lwip/apps/snmp_mib2.h"
+#include "lwip/apps/snmp_table.h"
+#include "lwip/apps/snmp_scalar.h"
+#include "lwip/icmp.h"
+#include "lwip/stats.h"
+
+#if LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_ICMP
+
+#if SNMP_USE_NETCONN
+#define SYNC_NODE_NAME(node_name) node_name ## _synced
+#define CREATE_LWIP_SYNC_NODE(oid, node_name) \
+   static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks);
+#else
+#define SYNC_NODE_NAME(node_name) node_name
+#define CREATE_LWIP_SYNC_NODE(oid, node_name)
+#endif
+
+/* --- icmp .1.3.6.1.2.1.5 ----------------------------------------------------- */
+
+static s16_t
+icmp_get_value(const struct snmp_scalar_array_node_def *node, void *value)
+{
+  u32_t *uint_ptr = (u32_t*)value;
+
+  switch (node->oid) {
+  case 1: /* icmpInMsgs */
+    *uint_ptr = STATS_GET(mib2.icmpinmsgs);
+    return sizeof(*uint_ptr);
+  case 2: /* icmpInErrors */
+    *uint_ptr = STATS_GET(mib2.icmpinerrors);
+    return sizeof(*uint_ptr);
+  case 3: /* icmpInDestUnreachs */
+    *uint_ptr = STATS_GET(mib2.icmpindestunreachs);
+    return sizeof(*uint_ptr);
+  case 4: /* icmpInTimeExcds */
+    *uint_ptr = STATS_GET(mib2.icmpintimeexcds);
+    return sizeof(*uint_ptr);
+  case 5: /* icmpInParmProbs */
+    *uint_ptr = STATS_GET(mib2.icmpinparmprobs);
+    return sizeof(*uint_ptr);
+  case 6: /* icmpInSrcQuenchs */
+    *uint_ptr = STATS_GET(mib2.icmpinsrcquenchs);
+    return sizeof(*uint_ptr);
+  case 7: /* icmpInRedirects */
+    *uint_ptr = STATS_GET(mib2.icmpinredirects);
+    return sizeof(*uint_ptr);
+  case 8: /* icmpInEchos */
+    *uint_ptr = STATS_GET(mib2.icmpinechos);
+    return sizeof(*uint_ptr);
+  case 9: /* icmpInEchoReps */
+    *uint_ptr = STATS_GET(mib2.icmpinechoreps);
+    return sizeof(*uint_ptr);
+  case 10: /* icmpInTimestamps */
+    *uint_ptr = STATS_GET(mib2.icmpintimestamps);
+    return sizeof(*uint_ptr);
+  case 11: /* icmpInTimestampReps */
+    *uint_ptr = STATS_GET(mib2.icmpintimestampreps);
+    return sizeof(*uint_ptr);
+  case 12: /* icmpInAddrMasks */
+    *uint_ptr = STATS_GET(mib2.icmpinaddrmasks);
+    return sizeof(*uint_ptr);
+  case 13: /* icmpInAddrMaskReps */
+    *uint_ptr = STATS_GET(mib2.icmpinaddrmaskreps);
+    return sizeof(*uint_ptr);
+  case 14: /* icmpOutMsgs */
+    *uint_ptr = STATS_GET(mib2.icmpoutmsgs);
+    return sizeof(*uint_ptr);
+  case 15: /* icmpOutErrors */
+    *uint_ptr = STATS_GET(mib2.icmpouterrors);
+    return sizeof(*uint_ptr);
+  case 16: /* icmpOutDestUnreachs */
+    *uint_ptr = STATS_GET(mib2.icmpoutdestunreachs);
+    return sizeof(*uint_ptr);
+  case 17: /* icmpOutTimeExcds */
+    *uint_ptr = STATS_GET(mib2.icmpouttimeexcds);
+    return sizeof(*uint_ptr);
+  case 18: /* icmpOutParmProbs: not supported -> always 0 */
+    *uint_ptr = 0;
+    return sizeof(*uint_ptr);
+  case 19: /* icmpOutSrcQuenchs: not supported -> always 0 */
+    *uint_ptr = 0;
+    return sizeof(*uint_ptr);
+  case 20: /* icmpOutRedirects: not supported -> always 0 */
+    *uint_ptr = 0;
+    return sizeof(*uint_ptr);
+  case 21: /* icmpOutEchos */
+    *uint_ptr = STATS_GET(mib2.icmpoutechos);
+    return sizeof(*uint_ptr);
+  case 22: /* icmpOutEchoReps */
+    *uint_ptr = STATS_GET(mib2.icmpoutechoreps);
+    return sizeof(*uint_ptr);
+  case 23: /* icmpOutTimestamps: not supported -> always 0 */
+    *uint_ptr = 0;
+    return sizeof(*uint_ptr);
+  case 24: /* icmpOutTimestampReps: not supported -> always 0 */
+    *uint_ptr = 0;
+    return sizeof(*uint_ptr);
+  case 25: /* icmpOutAddrMasks: not supported -> always 0 */
+    *uint_ptr = 0;
+    return sizeof(*uint_ptr);
+  case 26: /* icmpOutAddrMaskReps: not supported -> always 0 */
+    *uint_ptr = 0;
+    return sizeof(*uint_ptr);
+  default:
+    LWIP_DEBUGF(SNMP_MIB_DEBUG,("icmp_get_value(): unknown id: %"S32_F"\n", node->oid));
+    break;
+  }
+
+  return 0;
+}
+
+
+static const struct snmp_scalar_array_node_def icmp_nodes[] = {
+  { 1, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  { 2, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  { 3, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  { 4, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  { 5, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  { 6, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  { 7, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  { 8, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  { 9, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  {10, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  {11, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  {12, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  {13, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  {14, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  {15, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  {16, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  {17, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  {18, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  {19, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  {20, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  {21, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  {22, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  {23, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  {24, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  {25, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+  {26, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}
+};
+
+const struct snmp_scalar_array_node snmp_mib2_icmp_root = SNMP_SCALAR_CREATE_ARRAY_NODE(5, icmp_nodes, icmp_get_value, NULL, NULL);
+
+#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_ICMP */

+ 375 - 368
libs/thirdparty/lwip_2.1.2/src/apps/snmp/snmp_mib2_interfaces.c → libs/thirdparty/LwIP/src/apps/snmp/snmp_mib2_interfaces.c

@@ -1,368 +1,375 @@
-/**
- * @file
- * Management Information Base II (RFC1213) INTERFACES objects and functions.
- */
-
-/*
- * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * Author: Dirk Ziegelmeier <dziegel@gmx.de>
- *         Christiaan Simons <christiaan.simons@axon.tv>
- */
-
-#include "lwip/snmp.h"
-#include "lwip/apps/snmp.h"
-#include "lwip/apps/snmp_core.h"
-#include "lwip/apps/snmp_mib2.h"
-#include "lwip/apps/snmp_table.h"
-#include "lwip/apps/snmp_scalar.h"
-#include "lwip/netif.h"
-#include "lwip/stats.h"
-
-#include <string.h>
-
-#if LWIP_SNMP && SNMP_LWIP_MIB2
-
-#if SNMP_USE_NETCONN
-#define SYNC_NODE_NAME(node_name) node_name ## _synced
-#define CREATE_LWIP_SYNC_NODE(oid, node_name) \
-   static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks);
-#else
-#define SYNC_NODE_NAME(node_name) node_name
-#define CREATE_LWIP_SYNC_NODE(oid, node_name)
-#endif
-
-
-/* --- interfaces .1.3.6.1.2.1.2 ----------------------------------------------------- */
-
-static s16_t
-interfaces_get_value(struct snmp_node_instance *instance, void *value)
-{
-  if (instance->node->oid == 1) {
-    s32_t *sint_ptr = (s32_t *)value;
-    s32_t num_netifs = 0;
-
-    struct netif *netif;
-    NETIF_FOREACH(netif) {
-      num_netifs++;
-    }
-
-    *sint_ptr = num_netifs;
-    return sizeof(*sint_ptr);
-  }
-
-  return 0;
-}
-
-/* list of allowed value ranges for incoming OID */
-static const struct snmp_oid_range interfaces_Table_oid_ranges[] = {
-  { 1, 0xff } /* netif->num is u8_t */
-};
-
-static const u8_t iftable_ifOutQLen         = 0;
-
-static const u8_t iftable_ifOperStatus_up   = 1;
-static const u8_t iftable_ifOperStatus_down = 2;
-
-static const u8_t iftable_ifAdminStatus_up             = 1;
-static const u8_t iftable_ifAdminStatus_lowerLayerDown = 7;
-static const u8_t iftable_ifAdminStatus_down           = 2;
-
-static snmp_err_t
-interfaces_Table_get_cell_instance(const u32_t *column, const u32_t *row_oid, u8_t row_oid_len, struct snmp_node_instance *cell_instance)
-{
-  u32_t ifIndex;
-  struct netif *netif;
-
-  LWIP_UNUSED_ARG(column);
-
-  /* check if incoming OID length and if values are in plausible range */
-  if (!snmp_oid_in_range(row_oid, row_oid_len, interfaces_Table_oid_ranges, LWIP_ARRAYSIZE(interfaces_Table_oid_ranges))) {
-    return SNMP_ERR_NOSUCHINSTANCE;
-  }
-
-  /* get netif index from incoming OID */
-  ifIndex = row_oid[0];
-
-  /* find netif with index */
-  NETIF_FOREACH(netif) {
-    if (netif_to_num(netif) == ifIndex) {
-      /* store netif pointer for subsequent operations (get/test/set) */
-      cell_instance->reference.ptr = netif;
-      return SNMP_ERR_NOERROR;
-    }
-  }
-
-  /* not found */
-  return SNMP_ERR_NOSUCHINSTANCE;
-}
-
-static snmp_err_t
-interfaces_Table_get_next_cell_instance(const u32_t *column, struct snmp_obj_id *row_oid, struct snmp_node_instance *cell_instance)
-{
-  struct netif *netif;
-  struct snmp_next_oid_state state;
-  u32_t result_temp[LWIP_ARRAYSIZE(interfaces_Table_oid_ranges)];
-
-  LWIP_UNUSED_ARG(column);
-
-  /* init struct to search next oid */
-  snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(interfaces_Table_oid_ranges));
-
-  /* iterate over all possible OIDs to find the next one */
-  NETIF_FOREACH(netif) {
-    u32_t test_oid[LWIP_ARRAYSIZE(interfaces_Table_oid_ranges)];
-    test_oid[0] = netif_to_num(netif);
-
-    /* check generated OID: is it a candidate for the next one? */
-    snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(interfaces_Table_oid_ranges), netif);
-  }
-
-  /* did we find a next one? */
-  if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
-    snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
-    /* store netif pointer for subsequent operations (get/test/set) */
-    cell_instance->reference.ptr = /* (struct netif*) */state.reference;
-    return SNMP_ERR_NOERROR;
-  }
-
-  /* not found */
-  return SNMP_ERR_NOSUCHINSTANCE;
-}
-
-static s16_t
-interfaces_Table_get_value(struct snmp_node_instance *instance, void *value)
-{
-  struct netif *netif = (struct netif *)instance->reference.ptr;
-  u32_t *value_u32 = (u32_t *)value;
-  s32_t *value_s32 = (s32_t *)value;
-  u16_t value_len;
-
-  switch (SNMP_TABLE_GET_COLUMN_FROM_OID(instance->instance_oid.id)) {
-    case 1: /* ifIndex */
-      *value_s32 = netif_to_num(netif);
-      value_len = sizeof(*value_s32);
-      break;
-    case 2: /* ifDescr */
-      value_len = sizeof(netif->name);
-      MEMCPY(value, netif->name, value_len);
-      break;
-    case 3: /* ifType */
-      *value_s32 = netif->link_type;
-      value_len = sizeof(*value_s32);
-      break;
-    case 4: /* ifMtu */
-      *value_s32 = netif->mtu;
-      value_len = sizeof(*value_s32);
-      break;
-    case 5: /* ifSpeed */
-      *value_u32 = netif->link_speed;
-      value_len = sizeof(*value_u32);
-      break;
-    case 6: /* ifPhysAddress */
-      value_len = sizeof(netif->hwaddr);
-      MEMCPY(value, &netif->hwaddr, value_len);
-      break;
-    case 7: /* ifAdminStatus */
-      if (netif_is_up(netif)) {
-        *value_s32 = iftable_ifOperStatus_up;
-      } else {
-        *value_s32 = iftable_ifOperStatus_down;
-      }
-      value_len = sizeof(*value_s32);
-      break;
-    case 8: /* ifOperStatus */
-      if (netif_is_up(netif)) {
-        if (netif_is_link_up(netif)) {
-          *value_s32 = iftable_ifAdminStatus_up;
-        } else {
-          *value_s32 = iftable_ifAdminStatus_lowerLayerDown;
-        }
-      } else {
-        *value_s32 = iftable_ifAdminStatus_down;
-      }
-      value_len = sizeof(*value_s32);
-      break;
-    case 9: /* ifLastChange */
-      *value_u32 = netif->ts;
-      value_len = sizeof(*value_u32);
-      break;
-    case 10: /* ifInOctets */
-      *value_u32 = netif->mib2_counters.ifinoctets;
-      value_len = sizeof(*value_u32);
-      break;
-    case 11: /* ifInUcastPkts */
-      *value_u32 = netif->mib2_counters.ifinucastpkts;
-      value_len = sizeof(*value_u32);
-      break;
-    case 12: /* ifInNUcastPkts */
-      *value_u32 = netif->mib2_counters.ifinnucastpkts;
-      value_len = sizeof(*value_u32);
-      break;
-    case 13: /* ifInDiscards */
-      *value_u32 = netif->mib2_counters.ifindiscards;
-      value_len = sizeof(*value_u32);
-      break;
-    case 14: /* ifInErrors */
-      *value_u32 = netif->mib2_counters.ifinerrors;
-      value_len = sizeof(*value_u32);
-      break;
-    case 15: /* ifInUnkownProtos */
-      *value_u32 = netif->mib2_counters.ifinunknownprotos;
-      value_len = sizeof(*value_u32);
-      break;
-    case 16: /* ifOutOctets */
-      *value_u32 = netif->mib2_counters.ifoutoctets;
-      value_len = sizeof(*value_u32);
-      break;
-    case 17: /* ifOutUcastPkts */
-      *value_u32 = netif->mib2_counters.ifoutucastpkts;
-      value_len = sizeof(*value_u32);
-      break;
-    case 18: /* ifOutNUcastPkts */
-      *value_u32 = netif->mib2_counters.ifoutnucastpkts;
-      value_len = sizeof(*value_u32);
-      break;
-    case 19: /* ifOutDiscarts */
-      *value_u32 = netif->mib2_counters.ifoutdiscards;
-      value_len = sizeof(*value_u32);
-      break;
-    case 20: /* ifOutErrors */
-      *value_u32 = netif->mib2_counters.ifouterrors;
-      value_len = sizeof(*value_u32);
-      break;
-    case 21: /* ifOutQLen */
-      *value_u32 = iftable_ifOutQLen;
-      value_len = sizeof(*value_u32);
-      break;
-    /** @note returning zeroDotZero (0.0) no media specific MIB support */
-    case 22: /* ifSpecific */
-      value_len = snmp_zero_dot_zero.len * sizeof(u32_t);
-      MEMCPY(value, snmp_zero_dot_zero.id, value_len);
-      break;
-    default:
-      return 0;
-  }
-
-  return value_len;
-}
-
-#if !SNMP_SAFE_REQUESTS
-
-static snmp_err_t
-interfaces_Table_set_test(struct snmp_node_instance *instance, u16_t len, void *value)
-{
-  s32_t *sint_ptr = (s32_t *)value;
-
-  /* stack should never call this method for another column,
-  because all other columns are set to readonly */
-  LWIP_ASSERT("Invalid column", (SNMP_TABLE_GET_COLUMN_FROM_OID(instance->instance_oid.id) == 7));
-  LWIP_UNUSED_ARG(len);
-
-  if (*sint_ptr == 1 || *sint_ptr == 2) {
-    return SNMP_ERR_NOERROR;
-  }
-
-  return SNMP_ERR_WRONGVALUE;
-}
-
-static snmp_err_t
-interfaces_Table_set_value(struct snmp_node_instance *instance, u16_t len, void *value)
-{
-  struct netif *netif = (struct netif *)instance->reference.ptr;
-  s32_t *sint_ptr = (s32_t *)value;
-
-  /* stack should never call this method for another column,
-  because all other columns are set to readonly */
-  LWIP_ASSERT("Invalid column", (SNMP_TABLE_GET_COLUMN_FROM_OID(instance->instance_oid.id) == 7));
-  LWIP_UNUSED_ARG(len);
-
-  if (*sint_ptr == 1) {
-    netif_set_up(netif);
-  } else if (*sint_ptr == 2) {
-    netif_set_down(netif);
-  }
-
-  return SNMP_ERR_NOERROR;
-}
-
-#endif /* SNMP_SAFE_REQUESTS */
-
-static const struct snmp_scalar_node interfaces_Number = SNMP_SCALAR_CREATE_NODE_READONLY(1, SNMP_ASN1_TYPE_INTEGER, interfaces_get_value);
-
-static const struct snmp_table_col_def interfaces_Table_columns[] = {
-  {  1, SNMP_ASN1_TYPE_INTEGER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifIndex */
-  {  2, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifDescr */
-  {  3, SNMP_ASN1_TYPE_INTEGER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifType */
-  {  4, SNMP_ASN1_TYPE_INTEGER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifMtu */
-  {  5, SNMP_ASN1_TYPE_GAUGE,        SNMP_NODE_INSTANCE_READ_ONLY }, /* ifSpeed */
-  {  6, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifPhysAddress */
-#if !SNMP_SAFE_REQUESTS
-  {  7, SNMP_ASN1_TYPE_INTEGER,      SNMP_NODE_INSTANCE_READ_WRITE }, /* ifAdminStatus */
-#else
-  {  7, SNMP_ASN1_TYPE_INTEGER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifAdminStatus */
-#endif
-  {  8, SNMP_ASN1_TYPE_INTEGER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOperStatus */
-  {  9, SNMP_ASN1_TYPE_TIMETICKS,    SNMP_NODE_INSTANCE_READ_ONLY }, /* ifLastChange */
-  { 10, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInOctets */
-  { 11, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInUcastPkts */
-  { 12, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInNUcastPkts */
-  { 13, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInDiscarts */
-  { 14, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInErrors */
-  { 15, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInUnkownProtos */
-  { 16, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutOctets */
-  { 17, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutUcastPkts */
-  { 18, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutNUcastPkts */
-  { 19, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutDiscarts */
-  { 20, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutErrors */
-  { 21, SNMP_ASN1_TYPE_GAUGE,        SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutQLen */
-  { 22, SNMP_ASN1_TYPE_OBJECT_ID,    SNMP_NODE_INSTANCE_READ_ONLY }  /* ifSpecific */
-};
-
-#if !SNMP_SAFE_REQUESTS
-static const struct snmp_table_node interfaces_Table = SNMP_TABLE_CREATE(
-      2, interfaces_Table_columns,
-      interfaces_Table_get_cell_instance, interfaces_Table_get_next_cell_instance,
-      interfaces_Table_get_value, interfaces_Table_set_test, interfaces_Table_set_value);
-#else
-static const struct snmp_table_node interfaces_Table = SNMP_TABLE_CREATE(
-      2, interfaces_Table_columns,
-      interfaces_Table_get_cell_instance, interfaces_Table_get_next_cell_instance,
-      interfaces_Table_get_value, NULL, NULL);
-#endif
-
-/* the following nodes access variables in LWIP stack from SNMP worker thread and must therefore be synced to LWIP (TCPIP) thread */
-CREATE_LWIP_SYNC_NODE(1, interfaces_Number)
-CREATE_LWIP_SYNC_NODE(2, interfaces_Table)
-
-static const struct snmp_node *const interface_nodes[] = {
-  &SYNC_NODE_NAME(interfaces_Number).node.node,
-  &SYNC_NODE_NAME(interfaces_Table).node.node
-};
-
-const struct snmp_tree_node snmp_mib2_interface_root = SNMP_CREATE_TREE_NODE(2, interface_nodes);
-
-#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 */
+/**
+ * @file
+ * Management Information Base II (RFC1213) INTERFACES objects and functions.
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Dirk Ziegelmeier <dziegel@gmx.de>
+ *         Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#include "lwip/snmp.h"
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "lwip/apps/snmp_mib2.h"
+#include "lwip/apps/snmp_table.h"
+#include "lwip/apps/snmp_scalar.h"
+#include "lwip/netif.h"
+#include "lwip/stats.h"
+
+#include <string.h>
+
+#if LWIP_SNMP && SNMP_LWIP_MIB2
+
+#if SNMP_USE_NETCONN
+#define SYNC_NODE_NAME(node_name) node_name ## _synced
+#define CREATE_LWIP_SYNC_NODE(oid, node_name) \
+   static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks);
+#else
+#define SYNC_NODE_NAME(node_name) node_name
+#define CREATE_LWIP_SYNC_NODE(oid, node_name)
+#endif
+
+
+/* --- interfaces .1.3.6.1.2.1.2 ----------------------------------------------------- */
+
+static s16_t
+interfaces_get_value(struct snmp_node_instance* instance, void* value)
+{
+  if (instance->node->oid == 1) {
+    s32_t *sint_ptr = (s32_t*)value;
+    s32_t num_netifs = 0;
+
+    struct netif *netif = netif_list;
+    while (netif != NULL) {
+      num_netifs++;
+      netif = netif->next;
+    }
+
+    *sint_ptr = num_netifs;
+    return sizeof(*sint_ptr);
+  }
+
+  return 0;
+}
+
+/* list of allowed value ranges for incoming OID */
+static const struct snmp_oid_range interfaces_Table_oid_ranges[] = {
+  { 1, 0xff } /* netif->num is u8_t */
+};
+
+static const u8_t iftable_ifOutQLen         = 0;
+
+static const u8_t iftable_ifOperStatus_up   = 1;
+static const u8_t iftable_ifOperStatus_down = 2;
+
+static const u8_t iftable_ifAdminStatus_up             = 1;
+static const u8_t iftable_ifAdminStatus_lowerLayerDown = 7;
+static const u8_t iftable_ifAdminStatus_down           = 2;
+
+static snmp_err_t
+interfaces_Table_get_cell_instance(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, struct snmp_node_instance* cell_instance)
+{
+  u32_t ifIndex;
+  struct netif *netif;
+
+  LWIP_UNUSED_ARG(column);
+
+  /* check if incoming OID length and if values are in plausible range */
+  if (!snmp_oid_in_range(row_oid, row_oid_len, interfaces_Table_oid_ranges, LWIP_ARRAYSIZE(interfaces_Table_oid_ranges))) {
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  /* get netif index from incoming OID */
+  ifIndex = row_oid[0];
+
+  /* find netif with index */
+  netif = netif_list;
+  while (netif != NULL) {
+    if (netif_to_num(netif) == ifIndex) {
+      /* store netif pointer for subsequent operations (get/test/set) */
+      cell_instance->reference.ptr = netif;
+      return SNMP_ERR_NOERROR;
+    }
+    netif = netif->next;
+  }
+
+  /* not found */
+  return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+static snmp_err_t
+interfaces_Table_get_next_cell_instance(const u32_t* column, struct snmp_obj_id* row_oid, struct snmp_node_instance* cell_instance)
+{
+  struct netif *netif;
+  struct snmp_next_oid_state state;
+  u32_t result_temp[LWIP_ARRAYSIZE(interfaces_Table_oid_ranges)];
+
+  LWIP_UNUSED_ARG(column);
+
+  /* init struct to search next oid */
+  snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(interfaces_Table_oid_ranges));
+
+  /* iterate over all possible OIDs to find the next one */
+  netif = netif_list;
+  while (netif != NULL) {
+    u32_t test_oid[LWIP_ARRAYSIZE(interfaces_Table_oid_ranges)];
+    test_oid[0] = netif_to_num(netif);
+
+    /* check generated OID: is it a candidate for the next one? */
+    snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(interfaces_Table_oid_ranges), netif);
+
+    netif = netif->next;
+  }
+
+  /* did we find a next one? */
+  if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
+    snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
+    /* store netif pointer for subsequent operations (get/test/set) */
+    cell_instance->reference.ptr = /* (struct netif*) */state.reference;
+    return SNMP_ERR_NOERROR;
+  }
+
+  /* not found */
+  return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+static s16_t
+interfaces_Table_get_value(struct snmp_node_instance* instance, void* value)
+{
+  struct netif *netif = (struct netif*)instance->reference.ptr;
+  u32_t* value_u32 = (u32_t*)value;
+  s32_t* value_s32 = (s32_t*)value;
+  u16_t value_len;
+
+  switch (SNMP_TABLE_GET_COLUMN_FROM_OID(instance->instance_oid.id))
+  {
+  case 1: /* ifIndex */
+    *value_s32 = netif_to_num(netif);
+    value_len = sizeof(*value_s32);
+    break;
+  case 2: /* ifDescr */
+    value_len = sizeof(netif->name);
+    MEMCPY(value, netif->name, value_len);
+    break;
+  case 3: /* ifType */
+    *value_s32 = netif->link_type;
+    value_len = sizeof(*value_s32);
+    break;
+  case 4: /* ifMtu */
+    *value_s32 = netif->mtu;
+    value_len = sizeof(*value_s32);
+    break;
+  case 5: /* ifSpeed */
+    *value_u32 = netif->link_speed;
+    value_len = sizeof(*value_u32);
+    break;
+  case 6: /* ifPhysAddress */
+    value_len = sizeof(netif->hwaddr);
+    MEMCPY(value, &netif->hwaddr, value_len);
+    break;
+  case 7: /* ifAdminStatus */
+    if (netif_is_up(netif)) {
+      *value_s32 = iftable_ifOperStatus_up;
+    } else {
+      *value_s32 = iftable_ifOperStatus_down;
+    }
+    value_len = sizeof(*value_s32);
+    break;
+  case 8: /* ifOperStatus */
+    if (netif_is_up(netif)) {
+      if (netif_is_link_up(netif)) {
+        *value_s32 = iftable_ifAdminStatus_up;
+      } else {
+        *value_s32 = iftable_ifAdminStatus_lowerLayerDown;
+      }
+    } else {
+      *value_s32 = iftable_ifAdminStatus_down;
+    }
+    value_len = sizeof(*value_s32);
+    break;
+  case 9: /* ifLastChange */
+    *value_u32 = netif->ts;
+    value_len = sizeof(*value_u32);
+    break;
+  case 10: /* ifInOctets */
+    *value_u32 = netif->mib2_counters.ifinoctets;
+    value_len = sizeof(*value_u32);
+    break;
+  case 11: /* ifInUcastPkts */
+    *value_u32 = netif->mib2_counters.ifinucastpkts;
+    value_len = sizeof(*value_u32);
+    break;
+  case 12: /* ifInNUcastPkts */
+    *value_u32 = netif->mib2_counters.ifinnucastpkts;
+    value_len = sizeof(*value_u32);
+    break;
+  case 13: /* ifInDiscards */
+    *value_u32 = netif->mib2_counters.ifindiscards;
+    value_len = sizeof(*value_u32);
+    break;
+  case 14: /* ifInErrors */
+    *value_u32 = netif->mib2_counters.ifinerrors;
+    value_len = sizeof(*value_u32);
+    break;
+  case 15: /* ifInUnkownProtos */
+    *value_u32 = netif->mib2_counters.ifinunknownprotos;
+    value_len = sizeof(*value_u32);
+    break;
+  case 16: /* ifOutOctets */
+    *value_u32 = netif->mib2_counters.ifoutoctets;
+    value_len = sizeof(*value_u32);
+    break;
+  case 17: /* ifOutUcastPkts */
+    *value_u32 = netif->mib2_counters.ifoutucastpkts;
+    value_len = sizeof(*value_u32);
+    break;
+  case 18: /* ifOutNUcastPkts */
+    *value_u32 = netif->mib2_counters.ifoutnucastpkts;
+    value_len = sizeof(*value_u32);
+    break;
+  case 19: /* ifOutDiscarts */
+    *value_u32 = netif->mib2_counters.ifoutdiscards;
+    value_len = sizeof(*value_u32);
+    break;
+  case 20: /* ifOutErrors */
+    *value_u32 = netif->mib2_counters.ifouterrors;
+    value_len = sizeof(*value_u32);
+    break;
+  case 21: /* ifOutQLen */
+    *value_u32 = iftable_ifOutQLen;
+    value_len = sizeof(*value_u32);
+    break;
+  /** @note returning zeroDotZero (0.0) no media specific MIB support */
+  case 22: /* ifSpecific */
+    value_len = snmp_zero_dot_zero.len * sizeof(u32_t);
+    MEMCPY(value, snmp_zero_dot_zero.id, value_len);
+    break;
+  default:
+    return 0;
+  }
+
+  return value_len;
+}
+
+#if !SNMP_SAFE_REQUESTS
+
+static snmp_err_t
+interfaces_Table_set_test(struct snmp_node_instance* instance, u16_t len, void *value)
+{
+  s32_t *sint_ptr = (s32_t*)value;
+
+  /* stack should never call this method for another column,
+  because all other columns are set to readonly */
+  LWIP_ASSERT("Invalid column", (SNMP_TABLE_GET_COLUMN_FROM_OID(instance->instance_oid.id) == 7));
+  LWIP_UNUSED_ARG(len);
+
+  if (*sint_ptr == 1 || *sint_ptr == 2) {
+    return SNMP_ERR_NOERROR;
+  }
+
+  return SNMP_ERR_WRONGVALUE;
+}
+
+static snmp_err_t
+interfaces_Table_set_value(struct snmp_node_instance* instance, u16_t len, void *value)
+{
+  struct netif *netif = (struct netif*)instance->reference.ptr;
+  s32_t *sint_ptr = (s32_t*)value;
+
+  /* stack should never call this method for another column,
+  because all other columns are set to readonly */
+  LWIP_ASSERT("Invalid column", (SNMP_TABLE_GET_COLUMN_FROM_OID(instance->instance_oid.id) == 7));
+  LWIP_UNUSED_ARG(len);
+
+  if (*sint_ptr == 1) {
+    netif_set_up(netif);
+  } else if (*sint_ptr == 2) {
+    netif_set_down(netif);
+  }
+
+  return SNMP_ERR_NOERROR;
+}
+
+#endif /* SNMP_SAFE_REQUESTS */
+
+static const struct snmp_scalar_node interfaces_Number = SNMP_SCALAR_CREATE_NODE_READONLY(1, SNMP_ASN1_TYPE_INTEGER, interfaces_get_value);
+
+static const struct snmp_table_col_def interfaces_Table_columns[] = {
+  {  1, SNMP_ASN1_TYPE_INTEGER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifIndex */
+  {  2, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifDescr */
+  {  3, SNMP_ASN1_TYPE_INTEGER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifType */
+  {  4, SNMP_ASN1_TYPE_INTEGER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifMtu */
+  {  5, SNMP_ASN1_TYPE_GAUGE,        SNMP_NODE_INSTANCE_READ_ONLY }, /* ifSpeed */
+  {  6, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifPhysAddress */
+#if !SNMP_SAFE_REQUESTS
+  {  7, SNMP_ASN1_TYPE_INTEGER,      SNMP_NODE_INSTANCE_READ_WRITE }, /* ifAdminStatus */
+#else
+  {  7, SNMP_ASN1_TYPE_INTEGER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifAdminStatus */
+#endif
+  {  8, SNMP_ASN1_TYPE_INTEGER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOperStatus */
+  {  9, SNMP_ASN1_TYPE_TIMETICKS,    SNMP_NODE_INSTANCE_READ_ONLY }, /* ifLastChange */
+  { 10, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInOctets */
+  { 11, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInUcastPkts */
+  { 12, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInNUcastPkts */
+  { 13, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInDiscarts */
+  { 14, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInErrors */
+  { 15, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInUnkownProtos */
+  { 16, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutOctets */
+  { 17, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutUcastPkts */
+  { 18, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutNUcastPkts */
+  { 19, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutDiscarts */
+  { 20, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutErrors */
+  { 21, SNMP_ASN1_TYPE_GAUGE,        SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutQLen */
+  { 22, SNMP_ASN1_TYPE_OBJECT_ID,    SNMP_NODE_INSTANCE_READ_ONLY }  /* ifSpecific */
+};
+
+#if !SNMP_SAFE_REQUESTS
+static const struct snmp_table_node interfaces_Table = SNMP_TABLE_CREATE(
+  2, interfaces_Table_columns,
+  interfaces_Table_get_cell_instance, interfaces_Table_get_next_cell_instance,
+  interfaces_Table_get_value, interfaces_Table_set_test, interfaces_Table_set_value);
+#else
+static const struct snmp_table_node interfaces_Table = SNMP_TABLE_CREATE(
+  2, interfaces_Table_columns,
+  interfaces_Table_get_cell_instance, interfaces_Table_get_next_cell_instance,
+  interfaces_Table_get_value, NULL, NULL);
+#endif
+
+/* the following nodes access variables in LWIP stack from SNMP worker thread and must therefore be synced to LWIP (TCPIP) thread */
+CREATE_LWIP_SYNC_NODE(1, interfaces_Number)
+CREATE_LWIP_SYNC_NODE(2, interfaces_Table)
+
+static const struct snmp_node* const interface_nodes[] = {
+  &SYNC_NODE_NAME(interfaces_Number).node.node,
+  &SYNC_NODE_NAME(interfaces_Table).node.node
+};
+
+const struct snmp_tree_node snmp_mib2_interface_root = SNMP_CREATE_TREE_NODE(2, interface_nodes);
+
+#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 */

+ 743 - 731
libs/thirdparty/lwip_2.1.2/src/apps/snmp/snmp_mib2_ip.c → libs/thirdparty/LwIP/src/apps/snmp/snmp_mib2_ip.c

@@ -1,731 +1,743 @@
-/**
- * @file
- * Management Information Base II (RFC1213) IP objects and functions.
- */
-
-/*
- * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * Author: Dirk Ziegelmeier <dziegel@gmx.de>
- *         Christiaan Simons <christiaan.simons@axon.tv>
- */
-
-#include "lwip/snmp.h"
-#include "lwip/apps/snmp.h"
-#include "lwip/apps/snmp_core.h"
-#include "lwip/apps/snmp_mib2.h"
-#include "lwip/apps/snmp_table.h"
-#include "lwip/apps/snmp_scalar.h"
-#include "lwip/stats.h"
-#include "lwip/netif.h"
-#include "lwip/ip.h"
-#include "lwip/etharp.h"
-
-#if LWIP_SNMP && SNMP_LWIP_MIB2
-
-#if SNMP_USE_NETCONN
-#define SYNC_NODE_NAME(node_name) node_name ## _synced
-#define CREATE_LWIP_SYNC_NODE(oid, node_name) \
-   static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks);
-#else
-#define SYNC_NODE_NAME(node_name) node_name
-#define CREATE_LWIP_SYNC_NODE(oid, node_name)
-#endif
-
-#if LWIP_IPV4
-/* --- ip .1.3.6.1.2.1.4 ----------------------------------------------------- */
-
-static s16_t
-ip_get_value(struct snmp_node_instance *instance, void *value)
-{
-  s32_t *sint_ptr = (s32_t *)value;
-  u32_t *uint_ptr = (u32_t *)value;
-
-  switch (instance->node->oid) {
-    case 1: /* ipForwarding */
-#if IP_FORWARD
-      /* forwarding */
-      *sint_ptr = 1;
-#else
-      /* not-forwarding */
-      *sint_ptr = 2;
-#endif
-      return sizeof(*sint_ptr);
-    case 2: /* ipDefaultTTL */
-      *sint_ptr = IP_DEFAULT_TTL;
-      return sizeof(*sint_ptr);
-    case 3: /* ipInReceives */
-      *uint_ptr = STATS_GET(mib2.ipinreceives);
-      return sizeof(*uint_ptr);
-    case 4: /* ipInHdrErrors */
-      *uint_ptr = STATS_GET(mib2.ipinhdrerrors);
-      return sizeof(*uint_ptr);
-    case 5: /* ipInAddrErrors */
-      *uint_ptr = STATS_GET(mib2.ipinaddrerrors);
-      return sizeof(*uint_ptr);
-    case 6: /* ipForwDatagrams */
-      *uint_ptr = STATS_GET(mib2.ipforwdatagrams);
-      return sizeof(*uint_ptr);
-    case 7: /* ipInUnknownProtos */
-      *uint_ptr = STATS_GET(mib2.ipinunknownprotos);
-      return sizeof(*uint_ptr);
-    case 8: /* ipInDiscards */
-      *uint_ptr = STATS_GET(mib2.ipindiscards);
-      return sizeof(*uint_ptr);
-    case 9: /* ipInDelivers */
-      *uint_ptr = STATS_GET(mib2.ipindelivers);
-      return sizeof(*uint_ptr);
-    case 10: /* ipOutRequests */
-      *uint_ptr = STATS_GET(mib2.ipoutrequests);
-      return sizeof(*uint_ptr);
-    case 11: /* ipOutDiscards */
-      *uint_ptr = STATS_GET(mib2.ipoutdiscards);
-      return sizeof(*uint_ptr);
-    case 12: /* ipOutNoRoutes */
-      *uint_ptr = STATS_GET(mib2.ipoutnoroutes);
-      return sizeof(*uint_ptr);
-    case 13: /* ipReasmTimeout */
-#if IP_REASSEMBLY
-      *sint_ptr = IP_REASS_MAXAGE;
-#else
-      *sint_ptr = 0;
-#endif
-      return sizeof(*sint_ptr);
-    case 14: /* ipReasmReqds */
-      *uint_ptr = STATS_GET(mib2.ipreasmreqds);
-      return sizeof(*uint_ptr);
-    case 15: /* ipReasmOKs */
-      *uint_ptr = STATS_GET(mib2.ipreasmoks);
-      return sizeof(*uint_ptr);
-    case 16: /* ipReasmFails */
-      *uint_ptr = STATS_GET(mib2.ipreasmfails);
-      return sizeof(*uint_ptr);
-    case 17: /* ipFragOKs */
-      *uint_ptr = STATS_GET(mib2.ipfragoks);
-      return sizeof(*uint_ptr);
-    case 18: /* ipFragFails */
-      *uint_ptr = STATS_GET(mib2.ipfragfails);
-      return sizeof(*uint_ptr);
-    case 19: /* ipFragCreates */
-      *uint_ptr = STATS_GET(mib2.ipfragcreates);
-      return sizeof(*uint_ptr);
-    case 23: /* ipRoutingDiscards: not supported -> always 0 */
-      *uint_ptr = 0;
-      return sizeof(*uint_ptr);
-    default:
-      LWIP_DEBUGF(SNMP_MIB_DEBUG, ("ip_get_value(): unknown id: %"S32_F"\n", instance->node->oid));
-      break;
-  }
-
-  return 0;
-}
-
-/**
- * Test ip object value before setting.
- *
- * @param instance node instance
- * @param len return value space (in bytes)
- * @param value points to (varbind) space to copy value from.
- *
- * @note we allow set if the value matches the hardwired value,
- *   otherwise return badvalue.
- */
-static snmp_err_t
-ip_set_test(struct snmp_node_instance *instance, u16_t len, void *value)
-{
-  snmp_err_t ret = SNMP_ERR_WRONGVALUE;
-  s32_t *sint_ptr = (s32_t *)value;
-
-  LWIP_UNUSED_ARG(len);
-  switch (instance->node->oid) {
-    case 1: /* ipForwarding */
-#if IP_FORWARD
-      /* forwarding */
-      if (*sint_ptr == 1)
-#else
-      /* not-forwarding */
-      if (*sint_ptr == 2)
-#endif
-      {
-        ret = SNMP_ERR_NOERROR;
-      }
-      break;
-    case 2: /* ipDefaultTTL */
-      if (*sint_ptr == IP_DEFAULT_TTL) {
-        ret = SNMP_ERR_NOERROR;
-      }
-      break;
-    default:
-      LWIP_DEBUGF(SNMP_MIB_DEBUG, ("ip_set_test(): unknown id: %"S32_F"\n", instance->node->oid));
-      break;
-  }
-
-  return ret;
-}
-
-static snmp_err_t
-ip_set_value(struct snmp_node_instance *instance, u16_t len, void *value)
-{
-  LWIP_UNUSED_ARG(instance);
-  LWIP_UNUSED_ARG(len);
-  LWIP_UNUSED_ARG(value);
-  /* nothing to do here because in set_test we only accept values being the same as our own stored value -> no need to store anything */
-  return SNMP_ERR_NOERROR;
-}
-
-/* --- ipAddrTable --- */
-
-/* list of allowed value ranges for incoming OID */
-static const struct snmp_oid_range ip_AddrTable_oid_ranges[] = {
-  { 0, 0xff }, /* IP A */
-  { 0, 0xff }, /* IP B */
-  { 0, 0xff }, /* IP C */
-  { 0, 0xff }  /* IP D */
-};
-
-static snmp_err_t
-ip_AddrTable_get_cell_value_core(struct netif *netif, const u32_t *column, union snmp_variant_value *value, u32_t *value_len)
-{
-  LWIP_UNUSED_ARG(value_len);
-
-  switch (*column) {
-    case 1: /* ipAdEntAddr */
-      value->u32 = netif_ip4_addr(netif)->addr;
-      break;
-    case 2: /* ipAdEntIfIndex */
-      value->u32 = netif_to_num(netif);
-      break;
-    case 3: /* ipAdEntNetMask */
-      value->u32 = netif_ip4_netmask(netif)->addr;
-      break;
-    case 4: /* ipAdEntBcastAddr */
-      /* lwIP oddity, there's no broadcast
-         address in the netif we can rely on */
-      value->u32 = IPADDR_BROADCAST & 1;
-      break;
-    case 5: /* ipAdEntReasmMaxSize */
-#if IP_REASSEMBLY
-      /* @todo The theoretical maximum is IP_REASS_MAX_PBUFS * size of the pbufs,
-       * but only if receiving one fragmented packet at a time.
-       * The current solution is to calculate for 2 simultaneous packets...
-       */
-      value->u32 = (IP_HLEN + ((IP_REASS_MAX_PBUFS / 2) *
-                               (PBUF_POOL_BUFSIZE - PBUF_LINK_ENCAPSULATION_HLEN - PBUF_LINK_HLEN - IP_HLEN)));
-#else
-      /** @todo returning MTU would be a bad thing and
-          returning a wild guess like '576' isn't good either */
-      value->u32 = 0;
-#endif
-      break;
-    default:
-      return SNMP_ERR_NOSUCHINSTANCE;
-  }
-
-  return SNMP_ERR_NOERROR;
-}
-
-static snmp_err_t
-ip_AddrTable_get_cell_value(const u32_t *column, const u32_t *row_oid, u8_t row_oid_len, union snmp_variant_value *value, u32_t *value_len)
-{
-  ip4_addr_t ip;
-  struct netif *netif;
-
-  /* check if incoming OID length and if values are in plausible range */
-  if (!snmp_oid_in_range(row_oid, row_oid_len, ip_AddrTable_oid_ranges, LWIP_ARRAYSIZE(ip_AddrTable_oid_ranges))) {
-    return SNMP_ERR_NOSUCHINSTANCE;
-  }
-
-  /* get IP from incoming OID */
-  snmp_oid_to_ip4(&row_oid[0], &ip); /* we know it succeeds because of oid_in_range check above */
-
-  /* find netif with requested ip */
-  NETIF_FOREACH(netif) {
-    if (ip4_addr_cmp(&ip, netif_ip4_addr(netif))) {
-      /* fill in object properties */
-      return ip_AddrTable_get_cell_value_core(netif, column, value, value_len);
-    }
-  }
-
-  /* not found */
-  return SNMP_ERR_NOSUCHINSTANCE;
-}
-
-static snmp_err_t
-ip_AddrTable_get_next_cell_instance_and_value(const u32_t *column, struct snmp_obj_id *row_oid, union snmp_variant_value *value, u32_t *value_len)
-{
-  struct netif *netif;
-  struct snmp_next_oid_state state;
-  u32_t result_temp[LWIP_ARRAYSIZE(ip_AddrTable_oid_ranges)];
-
-  /* init struct to search next oid */
-  snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(ip_AddrTable_oid_ranges));
-
-  /* iterate over all possible OIDs to find the next one */
-  NETIF_FOREACH(netif) {
-    u32_t test_oid[LWIP_ARRAYSIZE(ip_AddrTable_oid_ranges)];
-    snmp_ip4_to_oid(netif_ip4_addr(netif), &test_oid[0]);
-
-    /* check generated OID: is it a candidate for the next one? */
-    snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(ip_AddrTable_oid_ranges), netif);
-  }
-
-  /* did we find a next one? */
-  if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
-    snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
-    /* fill in object properties */
-    return ip_AddrTable_get_cell_value_core((struct netif *)state.reference, column, value, value_len);
-  }
-
-  /* not found */
-  return SNMP_ERR_NOSUCHINSTANCE;
-}
-
-/* --- ipRouteTable --- */
-
-/* list of allowed value ranges for incoming OID */
-static const struct snmp_oid_range ip_RouteTable_oid_ranges[] = {
-  { 0, 0xff }, /* IP A */
-  { 0, 0xff }, /* IP B */
-  { 0, 0xff }, /* IP C */
-  { 0, 0xff }, /* IP D */
-};
-
-static snmp_err_t
-ip_RouteTable_get_cell_value_core(struct netif *netif, u8_t default_route, const u32_t *column, union snmp_variant_value *value, u32_t *value_len)
-{
-  switch (*column) {
-    case 1: /* ipRouteDest */
-      if (default_route) {
-        /* default rte has 0.0.0.0 dest */
-        value->u32 = IP4_ADDR_ANY4->addr;
-      } else {
-        /* netifs have netaddress dest */
-        ip4_addr_t tmp;
-        ip4_addr_get_network(&tmp, netif_ip4_addr(netif), netif_ip4_netmask(netif));
-        value->u32 = tmp.addr;
-      }
-      break;
-    case 2: /* ipRouteIfIndex */
-      value->u32 = netif_to_num(netif);
-      break;
-    case 3: /* ipRouteMetric1 */
-      if (default_route) {
-        value->s32 = 1; /* default */
-      } else {
-        value->s32 = 0; /* normal */
-      }
-      break;
-    case 4: /* ipRouteMetric2 */
-    case 5: /* ipRouteMetric3 */
-    case 6: /* ipRouteMetric4 */
-      value->s32 = -1; /* none */
-      break;
-    case 7: /* ipRouteNextHop */
-      if (default_route) {
-        /* default rte: gateway */
-        value->u32 = netif_ip4_gw(netif)->addr;
-      } else {
-        /* other rtes: netif ip_addr  */
-        value->u32 = netif_ip4_addr(netif)->addr;
-      }
-      break;
-    case 8: /* ipRouteType */
-      if (default_route) {
-        /* default rte is indirect */
-        value->u32 = 4; /* indirect */
-      } else {
-        /* other rtes are direct */
-        value->u32 = 3; /* direct */
-      }
-      break;
-    case 9: /* ipRouteProto */
-      /* locally defined routes */
-      value->u32 = 2; /* local */
-      break;
-    case 10: /* ipRouteAge */
-      /* @todo (sysuptime - timestamp last change) / 100 */
-      value->u32 = 0;
-      break;
-    case 11: /* ipRouteMask */
-      if (default_route) {
-        /* default rte use 0.0.0.0 mask */
-        value->u32 = IP4_ADDR_ANY4->addr;
-      } else {
-        /* other rtes use netmask */
-        value->u32 = netif_ip4_netmask(netif)->addr;
-      }
-      break;
-    case 12: /* ipRouteMetric5 */
-      value->s32 = -1; /* none */
-      break;
-    case 13: /* ipRouteInfo */
-      value->const_ptr = snmp_zero_dot_zero.id;
-      *value_len = snmp_zero_dot_zero.len * sizeof(u32_t);
-      break;
-    default:
-      return SNMP_ERR_NOSUCHINSTANCE;
-  }
-
-  return SNMP_ERR_NOERROR;
-}
-
-static snmp_err_t
-ip_RouteTable_get_cell_value(const u32_t *column, const u32_t *row_oid, u8_t row_oid_len, union snmp_variant_value *value, u32_t *value_len)
-{
-  ip4_addr_t test_ip;
-  struct netif *netif;
-
-  /* check if incoming OID length and if values are in plausible range */
-  if (!snmp_oid_in_range(row_oid, row_oid_len, ip_RouteTable_oid_ranges, LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges))) {
-    return SNMP_ERR_NOSUCHINSTANCE;
-  }
-
-  /* get IP and port from incoming OID */
-  snmp_oid_to_ip4(&row_oid[0], &test_ip); /* we know it succeeds because of oid_in_range check above */
-
-  /* default route is on default netif */
-  if (ip4_addr_isany_val(test_ip) && (netif_default != NULL)) {
-    /* fill in object properties */
-    return ip_RouteTable_get_cell_value_core(netif_default, 1, column, value, value_len);
-  }
-
-  /* find netif with requested route */
-  NETIF_FOREACH(netif) {
-    ip4_addr_t dst;
-    ip4_addr_get_network(&dst, netif_ip4_addr(netif), netif_ip4_netmask(netif));
-
-    if (ip4_addr_cmp(&dst, &test_ip)) {
-      /* fill in object properties */
-      return ip_RouteTable_get_cell_value_core(netif, 0, column, value, value_len);
-    }
-  }
-
-  /* not found */
-  return SNMP_ERR_NOSUCHINSTANCE;
-}
-
-static snmp_err_t
-ip_RouteTable_get_next_cell_instance_and_value(const u32_t *column, struct snmp_obj_id *row_oid, union snmp_variant_value *value, u32_t *value_len)
-{
-  struct netif *netif;
-  struct snmp_next_oid_state state;
-  u32_t result_temp[LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges)];
-  u32_t test_oid[LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges)];
-
-  /* init struct to search next oid */
-  snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges));
-
-  /* check default route */
-  if (netif_default != NULL) {
-    snmp_ip4_to_oid(IP4_ADDR_ANY4, &test_oid[0]);
-    snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges), netif_default);
-  }
-
-  /* iterate over all possible OIDs to find the next one */
-  NETIF_FOREACH(netif) {
-    ip4_addr_t dst;
-    ip4_addr_get_network(&dst, netif_ip4_addr(netif), netif_ip4_netmask(netif));
-
-    /* check generated OID: is it a candidate for the next one? */
-    if (!ip4_addr_isany_val(dst)) {
-      snmp_ip4_to_oid(&dst, &test_oid[0]);
-      snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges), netif);
-    }
-  }
-
-  /* did we find a next one? */
-  if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
-    ip4_addr_t dst;
-    snmp_oid_to_ip4(&result_temp[0], &dst);
-    snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
-    /* fill in object properties */
-    return ip_RouteTable_get_cell_value_core((struct netif *)state.reference, ip4_addr_isany_val(dst), column, value, value_len);
-  } else {
-    /* not found */
-    return SNMP_ERR_NOSUCHINSTANCE;
-  }
-}
-
-#if LWIP_ARP && LWIP_IPV4
-/* --- ipNetToMediaTable --- */
-
-/* list of allowed value ranges for incoming OID */
-static const struct snmp_oid_range ip_NetToMediaTable_oid_ranges[] = {
-  { 1, 0xff }, /* IfIndex */
-  { 0, 0xff }, /* IP A    */
-  { 0, 0xff }, /* IP B    */
-  { 0, 0xff }, /* IP C    */
-  { 0, 0xff }  /* IP D    */
-};
-
-static snmp_err_t
-ip_NetToMediaTable_get_cell_value_core(size_t arp_table_index, const u32_t *column, union snmp_variant_value *value, u32_t *value_len)
-{
-  ip4_addr_t *ip;
-  struct netif *netif;
-  struct eth_addr *ethaddr;
-
-  etharp_get_entry(arp_table_index, &ip, &netif, &ethaddr);
-
-  /* value */
-  switch (*column) {
-    case 1: /* atIfIndex / ipNetToMediaIfIndex */
-      value->u32 = netif_to_num(netif);
-      break;
-    case 2: /* atPhysAddress / ipNetToMediaPhysAddress */
-      value->ptr = ethaddr;
-      *value_len = sizeof(*ethaddr);
-      break;
-    case 3: /* atNetAddress / ipNetToMediaNetAddress */
-      value->u32 = ip->addr;
-      break;
-    case 4: /* ipNetToMediaType */
-      value->u32 = 3; /* dynamic*/
-      break;
-    default:
-      return SNMP_ERR_NOSUCHINSTANCE;
-  }
-
-  return SNMP_ERR_NOERROR;
-}
-
-static snmp_err_t
-ip_NetToMediaTable_get_cell_value(const u32_t *column, const u32_t *row_oid, u8_t row_oid_len, union snmp_variant_value *value, u32_t *value_len)
-{
-  ip4_addr_t ip_in;
-  u8_t netif_index;
-  size_t i;
-
-  /* check if incoming OID length and if values are in plausible range */
-  if (!snmp_oid_in_range(row_oid, row_oid_len, ip_NetToMediaTable_oid_ranges, LWIP_ARRAYSIZE(ip_NetToMediaTable_oid_ranges))) {
-    return SNMP_ERR_NOSUCHINSTANCE;
-  }
-
-  /* get IP from incoming OID */
-  netif_index = (u8_t)row_oid[0];
-  snmp_oid_to_ip4(&row_oid[1], &ip_in); /* we know it succeeds because of oid_in_range check above */
-
-  /* find requested entry */
-  for (i = 0; i < ARP_TABLE_SIZE; i++) {
-    ip4_addr_t *ip;
-    struct netif *netif;
-    struct eth_addr *ethaddr;
-
-    if (etharp_get_entry(i, &ip, &netif, &ethaddr)) {
-      if ((netif_index == netif_to_num(netif)) && ip4_addr_cmp(&ip_in, ip)) {
-        /* fill in object properties */
-        return ip_NetToMediaTable_get_cell_value_core(i, column, value, value_len);
-      }
-    }
-  }
-
-  /* not found */
-  return SNMP_ERR_NOSUCHINSTANCE;
-}
-
-static snmp_err_t
-ip_NetToMediaTable_get_next_cell_instance_and_value(const u32_t *column, struct snmp_obj_id *row_oid, union snmp_variant_value *value, u32_t *value_len)
-{
-  size_t i;
-  struct snmp_next_oid_state state;
-  u32_t result_temp[LWIP_ARRAYSIZE(ip_NetToMediaTable_oid_ranges)];
-
-  /* init struct to search next oid */
-  snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(ip_NetToMediaTable_oid_ranges));
-
-  /* iterate over all possible OIDs to find the next one */
-  for (i = 0; i < ARP_TABLE_SIZE; i++) {
-    ip4_addr_t *ip;
-    struct netif *netif;
-    struct eth_addr *ethaddr;
-
-    if (etharp_get_entry(i, &ip, &netif, &ethaddr)) {
-      u32_t test_oid[LWIP_ARRAYSIZE(ip_NetToMediaTable_oid_ranges)];
-
-      test_oid[0] = netif_to_num(netif);
-      snmp_ip4_to_oid(ip, &test_oid[1]);
-
-      /* check generated OID: is it a candidate for the next one? */
-      snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(ip_NetToMediaTable_oid_ranges), LWIP_PTR_NUMERIC_CAST(void *, i));
-    }
-  }
-
-  /* did we find a next one? */
-  if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
-    snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
-    /* fill in object properties */
-    return ip_NetToMediaTable_get_cell_value_core(LWIP_PTR_NUMERIC_CAST(size_t, state.reference), column, value, value_len);
-  }
-
-  /* not found */
-  return SNMP_ERR_NOSUCHINSTANCE;
-}
-
-#endif /* LWIP_ARP && LWIP_IPV4 */
-
-static const struct snmp_scalar_node ip_Forwarding      = SNMP_SCALAR_CREATE_NODE(1, SNMP_NODE_INSTANCE_READ_WRITE, SNMP_ASN1_TYPE_INTEGER, ip_get_value, ip_set_test, ip_set_value);
-static const struct snmp_scalar_node ip_DefaultTTL      = SNMP_SCALAR_CREATE_NODE(2, SNMP_NODE_INSTANCE_READ_WRITE, SNMP_ASN1_TYPE_INTEGER, ip_get_value, ip_set_test, ip_set_value);
-static const struct snmp_scalar_node ip_InReceives      = SNMP_SCALAR_CREATE_NODE_READONLY(3, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
-static const struct snmp_scalar_node ip_InHdrErrors     = SNMP_SCALAR_CREATE_NODE_READONLY(4, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
-static const struct snmp_scalar_node ip_InAddrErrors    = SNMP_SCALAR_CREATE_NODE_READONLY(5, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
-static const struct snmp_scalar_node ip_ForwDatagrams   = SNMP_SCALAR_CREATE_NODE_READONLY(6, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
-static const struct snmp_scalar_node ip_InUnknownProtos = SNMP_SCALAR_CREATE_NODE_READONLY(7, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
-static const struct snmp_scalar_node ip_InDiscards      = SNMP_SCALAR_CREATE_NODE_READONLY(8, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
-static const struct snmp_scalar_node ip_InDelivers      = SNMP_SCALAR_CREATE_NODE_READONLY(9, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
-static const struct snmp_scalar_node ip_OutRequests     = SNMP_SCALAR_CREATE_NODE_READONLY(10, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
-static const struct snmp_scalar_node ip_OutDiscards     = SNMP_SCALAR_CREATE_NODE_READONLY(11, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
-static const struct snmp_scalar_node ip_OutNoRoutes     = SNMP_SCALAR_CREATE_NODE_READONLY(12, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
-static const struct snmp_scalar_node ip_ReasmTimeout    = SNMP_SCALAR_CREATE_NODE_READONLY(13, SNMP_ASN1_TYPE_INTEGER, ip_get_value);
-static const struct snmp_scalar_node ip_ReasmReqds      = SNMP_SCALAR_CREATE_NODE_READONLY(14, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
-static const struct snmp_scalar_node ip_ReasmOKs        = SNMP_SCALAR_CREATE_NODE_READONLY(15, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
-static const struct snmp_scalar_node ip_ReasmFails      = SNMP_SCALAR_CREATE_NODE_READONLY(16, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
-static const struct snmp_scalar_node ip_FragOKs         = SNMP_SCALAR_CREATE_NODE_READONLY(17, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
-static const struct snmp_scalar_node ip_FragFails       = SNMP_SCALAR_CREATE_NODE_READONLY(18, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
-static const struct snmp_scalar_node ip_FragCreates     = SNMP_SCALAR_CREATE_NODE_READONLY(19, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
-static const struct snmp_scalar_node ip_RoutingDiscards = SNMP_SCALAR_CREATE_NODE_READONLY(23, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
-
-static const struct snmp_table_simple_col_def ip_AddrTable_columns[] = {
-  { 1, SNMP_ASN1_TYPE_IPADDR,  SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipAdEntAddr */
-  { 2, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipAdEntIfIndex */
-  { 3, SNMP_ASN1_TYPE_IPADDR,  SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipAdEntNetMask */
-  { 4, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipAdEntBcastAddr */
-  { 5, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }  /* ipAdEntReasmMaxSize */
-};
-
-static const struct snmp_table_simple_node ip_AddrTable = SNMP_TABLE_CREATE_SIMPLE(20, ip_AddrTable_columns, ip_AddrTable_get_cell_value, ip_AddrTable_get_next_cell_instance_and_value);
-
-static const struct snmp_table_simple_col_def ip_RouteTable_columns[] = {
-  {  1, SNMP_ASN1_TYPE_IPADDR,    SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteDest */
-  {  2, SNMP_ASN1_TYPE_INTEGER,   SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteIfIndex */
-  {  3, SNMP_ASN1_TYPE_INTEGER,   SNMP_VARIANT_VALUE_TYPE_S32 }, /* ipRouteMetric1 */
-  {  4, SNMP_ASN1_TYPE_INTEGER,   SNMP_VARIANT_VALUE_TYPE_S32 }, /* ipRouteMetric2 */
-  {  5, SNMP_ASN1_TYPE_INTEGER,   SNMP_VARIANT_VALUE_TYPE_S32 }, /* ipRouteMetric3 */
-  {  6, SNMP_ASN1_TYPE_INTEGER,   SNMP_VARIANT_VALUE_TYPE_S32 }, /* ipRouteMetric4 */
-  {  7, SNMP_ASN1_TYPE_IPADDR,    SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteNextHop */
-  {  8, SNMP_ASN1_TYPE_INTEGER,   SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteType */
-  {  9, SNMP_ASN1_TYPE_INTEGER,   SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteProto */
-  { 10, SNMP_ASN1_TYPE_INTEGER,   SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteAge */
-  { 11, SNMP_ASN1_TYPE_IPADDR,    SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteMask */
-  { 12, SNMP_ASN1_TYPE_INTEGER,   SNMP_VARIANT_VALUE_TYPE_S32 }, /* ipRouteMetric5 */
-  { 13, SNMP_ASN1_TYPE_OBJECT_ID, SNMP_VARIANT_VALUE_TYPE_PTR }  /* ipRouteInfo */
-};
-
-static const struct snmp_table_simple_node ip_RouteTable = SNMP_TABLE_CREATE_SIMPLE(21, ip_RouteTable_columns, ip_RouteTable_get_cell_value, ip_RouteTable_get_next_cell_instance_and_value);
-#endif /* LWIP_IPV4 */
-
-#if LWIP_ARP && LWIP_IPV4
-static const struct snmp_table_simple_col_def ip_NetToMediaTable_columns[] = {
-  {  1, SNMP_ASN1_TYPE_INTEGER,      SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipNetToMediaIfIndex */
-  {  2, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_VARIANT_VALUE_TYPE_PTR }, /* ipNetToMediaPhysAddress */
-  {  3, SNMP_ASN1_TYPE_IPADDR,       SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipNetToMediaNetAddress */
-  {  4, SNMP_ASN1_TYPE_INTEGER,      SNMP_VARIANT_VALUE_TYPE_U32 }  /* ipNetToMediaType */
-};
-
-static const struct snmp_table_simple_node ip_NetToMediaTable = SNMP_TABLE_CREATE_SIMPLE(22, ip_NetToMediaTable_columns, ip_NetToMediaTable_get_cell_value, ip_NetToMediaTable_get_next_cell_instance_and_value);
-#endif /* LWIP_ARP && LWIP_IPV4 */
-
-#if LWIP_IPV4
-/* the following nodes access variables in LWIP stack from SNMP worker thread and must therefore be synced to LWIP (TCPIP) thread */
-CREATE_LWIP_SYNC_NODE( 1, ip_Forwarding)
-CREATE_LWIP_SYNC_NODE( 2, ip_DefaultTTL)
-CREATE_LWIP_SYNC_NODE( 3, ip_InReceives)
-CREATE_LWIP_SYNC_NODE( 4, ip_InHdrErrors)
-CREATE_LWIP_SYNC_NODE( 5, ip_InAddrErrors)
-CREATE_LWIP_SYNC_NODE( 6, ip_ForwDatagrams)
-CREATE_LWIP_SYNC_NODE( 7, ip_InUnknownProtos)
-CREATE_LWIP_SYNC_NODE( 8, ip_InDiscards)
-CREATE_LWIP_SYNC_NODE( 9, ip_InDelivers)
-CREATE_LWIP_SYNC_NODE(10, ip_OutRequests)
-CREATE_LWIP_SYNC_NODE(11, ip_OutDiscards)
-CREATE_LWIP_SYNC_NODE(12, ip_OutNoRoutes)
-CREATE_LWIP_SYNC_NODE(13, ip_ReasmTimeout)
-CREATE_LWIP_SYNC_NODE(14, ip_ReasmReqds)
-CREATE_LWIP_SYNC_NODE(15, ip_ReasmOKs)
-CREATE_LWIP_SYNC_NODE(15, ip_ReasmFails)
-CREATE_LWIP_SYNC_NODE(17, ip_FragOKs)
-CREATE_LWIP_SYNC_NODE(18, ip_FragFails)
-CREATE_LWIP_SYNC_NODE(19, ip_FragCreates)
-CREATE_LWIP_SYNC_NODE(20, ip_AddrTable)
-CREATE_LWIP_SYNC_NODE(21, ip_RouteTable)
-#if LWIP_ARP
-CREATE_LWIP_SYNC_NODE(22, ip_NetToMediaTable)
-#endif /* LWIP_ARP */
-CREATE_LWIP_SYNC_NODE(23, ip_RoutingDiscards)
-
-static const struct snmp_node *const ip_nodes[] = {
-  &SYNC_NODE_NAME(ip_Forwarding).node.node,
-  &SYNC_NODE_NAME(ip_DefaultTTL).node.node,
-  &SYNC_NODE_NAME(ip_InReceives).node.node,
-  &SYNC_NODE_NAME(ip_InHdrErrors).node.node,
-  &SYNC_NODE_NAME(ip_InAddrErrors).node.node,
-  &SYNC_NODE_NAME(ip_ForwDatagrams).node.node,
-  &SYNC_NODE_NAME(ip_InUnknownProtos).node.node,
-  &SYNC_NODE_NAME(ip_InDiscards).node.node,
-  &SYNC_NODE_NAME(ip_InDelivers).node.node,
-  &SYNC_NODE_NAME(ip_OutRequests).node.node,
-  &SYNC_NODE_NAME(ip_OutDiscards).node.node,
-  &SYNC_NODE_NAME(ip_OutNoRoutes).node.node,
-  &SYNC_NODE_NAME(ip_ReasmTimeout).node.node,
-  &SYNC_NODE_NAME(ip_ReasmReqds).node.node,
-  &SYNC_NODE_NAME(ip_ReasmOKs).node.node,
-  &SYNC_NODE_NAME(ip_ReasmFails).node.node,
-  &SYNC_NODE_NAME(ip_FragOKs).node.node,
-  &SYNC_NODE_NAME(ip_FragFails).node.node,
-  &SYNC_NODE_NAME(ip_FragCreates).node.node,
-  &SYNC_NODE_NAME(ip_AddrTable).node.node,
-  &SYNC_NODE_NAME(ip_RouteTable).node.node,
-#if LWIP_ARP
-  &SYNC_NODE_NAME(ip_NetToMediaTable).node.node,
-#endif /* LWIP_ARP */
-  &SYNC_NODE_NAME(ip_RoutingDiscards).node.node
-};
-
-const struct snmp_tree_node snmp_mib2_ip_root = SNMP_CREATE_TREE_NODE(4, ip_nodes);
-#endif /* LWIP_IPV4 */
-
-/* --- at .1.3.6.1.2.1.3 ----------------------------------------------------- */
-
-#if LWIP_ARP && LWIP_IPV4
-/* at node table is a subset of ip_nettomedia table (same rows but less columns) */
-static const struct snmp_table_simple_col_def at_Table_columns[] = {
-  { 1, SNMP_ASN1_TYPE_INTEGER,      SNMP_VARIANT_VALUE_TYPE_U32 }, /* atIfIndex */
-  { 2, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_VARIANT_VALUE_TYPE_PTR }, /* atPhysAddress */
-  { 3, SNMP_ASN1_TYPE_IPADDR,       SNMP_VARIANT_VALUE_TYPE_U32 }  /* atNetAddress */
-};
-
-static const struct snmp_table_simple_node at_Table = SNMP_TABLE_CREATE_SIMPLE(1, at_Table_columns, ip_NetToMediaTable_get_cell_value, ip_NetToMediaTable_get_next_cell_instance_and_value);
-
-/* the following nodes access variables in LWIP stack from SNMP worker thread and must therefore be synced to LWIP (TCPIP) thread */
-CREATE_LWIP_SYNC_NODE(1, at_Table)
-
-static const struct snmp_node *const at_nodes[] = {
-  &SYNC_NODE_NAME(at_Table).node.node
-};
-
-const struct snmp_tree_node snmp_mib2_at_root = SNMP_CREATE_TREE_NODE(3, at_nodes);
-#endif /* LWIP_ARP && LWIP_IPV4 */
-
-#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 */
+/**
+ * @file
+ * Management Information Base II (RFC1213) IP objects and functions.
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Dirk Ziegelmeier <dziegel@gmx.de>
+ *         Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#include "lwip/snmp.h"
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "lwip/apps/snmp_mib2.h"
+#include "lwip/apps/snmp_table.h"
+#include "lwip/apps/snmp_scalar.h"
+#include "lwip/stats.h"
+#include "lwip/netif.h"
+#include "lwip/ip.h"
+#include "lwip/etharp.h"
+
+#if LWIP_SNMP && SNMP_LWIP_MIB2
+
+#if SNMP_USE_NETCONN
+#define SYNC_NODE_NAME(node_name) node_name ## _synced
+#define CREATE_LWIP_SYNC_NODE(oid, node_name) \
+   static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks);
+#else
+#define SYNC_NODE_NAME(node_name) node_name
+#define CREATE_LWIP_SYNC_NODE(oid, node_name)
+#endif
+
+#if LWIP_IPV4
+/* --- ip .1.3.6.1.2.1.4 ----------------------------------------------------- */
+
+static s16_t
+ip_get_value(struct snmp_node_instance* instance, void* value)
+{
+  s32_t* sint_ptr = (s32_t*)value;
+  u32_t* uint_ptr = (u32_t*)value;
+
+  switch (instance->node->oid) {
+  case 1: /* ipForwarding */
+#if IP_FORWARD
+    /* forwarding */
+    *sint_ptr = 1;
+#else
+    /* not-forwarding */
+    *sint_ptr = 2;
+#endif
+    return sizeof(*sint_ptr);
+  case 2: /* ipDefaultTTL */
+    *sint_ptr = IP_DEFAULT_TTL;
+    return sizeof(*sint_ptr);
+  case 3: /* ipInReceives */
+    *uint_ptr = STATS_GET(mib2.ipinreceives);
+    return sizeof(*uint_ptr);
+  case 4: /* ipInHdrErrors */
+    *uint_ptr = STATS_GET(mib2.ipinhdrerrors);
+    return sizeof(*uint_ptr);
+  case 5: /* ipInAddrErrors */
+    *uint_ptr = STATS_GET(mib2.ipinaddrerrors);
+    return sizeof(*uint_ptr);
+  case 6: /* ipForwDatagrams */
+    *uint_ptr = STATS_GET(mib2.ipforwdatagrams);
+    return sizeof(*uint_ptr);
+  case 7: /* ipInUnknownProtos */
+    *uint_ptr = STATS_GET(mib2.ipinunknownprotos);
+    return sizeof(*uint_ptr);
+  case 8: /* ipInDiscards */
+    *uint_ptr = STATS_GET(mib2.ipindiscards);
+    return sizeof(*uint_ptr);
+  case 9: /* ipInDelivers */
+    *uint_ptr = STATS_GET(mib2.ipindelivers);
+    return sizeof(*uint_ptr);
+  case 10: /* ipOutRequests */
+    *uint_ptr = STATS_GET(mib2.ipoutrequests);
+    return sizeof(*uint_ptr);
+  case 11: /* ipOutDiscards */
+    *uint_ptr = STATS_GET(mib2.ipoutdiscards);
+    return sizeof(*uint_ptr);
+  case 12: /* ipOutNoRoutes */
+    *uint_ptr = STATS_GET(mib2.ipoutnoroutes);
+    return sizeof(*uint_ptr);
+  case 13: /* ipReasmTimeout */
+#if IP_REASSEMBLY
+    *sint_ptr = IP_REASS_MAXAGE;
+#else
+    *sint_ptr = 0;
+#endif
+    return sizeof(*sint_ptr);
+  case 14: /* ipReasmReqds */
+    *uint_ptr = STATS_GET(mib2.ipreasmreqds);
+    return sizeof(*uint_ptr);
+  case 15: /* ipReasmOKs */
+    *uint_ptr = STATS_GET(mib2.ipreasmoks);
+    return sizeof(*uint_ptr);
+  case 16: /* ipReasmFails */
+    *uint_ptr = STATS_GET(mib2.ipreasmfails);
+    return sizeof(*uint_ptr);
+  case 17: /* ipFragOKs */
+    *uint_ptr = STATS_GET(mib2.ipfragoks);
+    return sizeof(*uint_ptr);
+  case 18: /* ipFragFails */
+    *uint_ptr = STATS_GET(mib2.ipfragfails);
+    return sizeof(*uint_ptr);
+  case 19: /* ipFragCreates */
+    *uint_ptr = STATS_GET(mib2.ipfragcreates);
+    return sizeof(*uint_ptr);
+  case 23: /* ipRoutingDiscards: not supported -> always 0 */
+    *uint_ptr = 0;
+    return sizeof(*uint_ptr);
+  default:
+    LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_get_value(): unknown id: %"S32_F"\n", instance->node->oid));
+    break;
+  }
+
+  return 0;
+}
+
+/**
+ * Test ip object value before setting.
+ *
+ * @param instance node instance
+ * @param len return value space (in bytes)
+ * @param value points to (varbind) space to copy value from.
+ *
+ * @note we allow set if the value matches the hardwired value,
+ *   otherwise return badvalue.
+ */
+static snmp_err_t
+ip_set_test(struct snmp_node_instance* instance, u16_t len, void *value)
+{
+  snmp_err_t ret = SNMP_ERR_WRONGVALUE;
+  s32_t *sint_ptr = (s32_t*)value;
+
+  LWIP_UNUSED_ARG(len);
+  switch (instance->node->oid) {
+  case 1: /* ipForwarding */
+#if IP_FORWARD
+    /* forwarding */
+    if (*sint_ptr == 1)
+#else
+    /* not-forwarding */
+    if (*sint_ptr == 2)
+#endif
+    {
+      ret = SNMP_ERR_NOERROR;
+    }
+    break;
+  case 2: /* ipDefaultTTL */
+    if (*sint_ptr == IP_DEFAULT_TTL) {
+      ret = SNMP_ERR_NOERROR;
+    }
+    break;
+  default:
+    LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_set_test(): unknown id: %"S32_F"\n", instance->node->oid));
+    break;
+  }
+
+  return ret;
+}
+
+static snmp_err_t
+ip_set_value(struct snmp_node_instance* instance, u16_t len, void *value)
+{
+  LWIP_UNUSED_ARG(instance);
+  LWIP_UNUSED_ARG(len);
+  LWIP_UNUSED_ARG(value);
+  /* nothing to do here because in set_test we only accept values being the same as our own stored value -> no need to store anything */
+  return SNMP_ERR_NOERROR;
+}
+
+/* --- ipAddrTable --- */
+
+/* list of allowed value ranges for incoming OID */
+static const struct snmp_oid_range ip_AddrTable_oid_ranges[] = {
+  { 0, 0xff }, /* IP A */
+  { 0, 0xff }, /* IP B */
+  { 0, 0xff }, /* IP C */
+  { 0, 0xff }  /* IP D */
+};
+
+static snmp_err_t
+ip_AddrTable_get_cell_value_core(struct netif *netif, const u32_t* column, union snmp_variant_value* value, u32_t* value_len)
+{
+  LWIP_UNUSED_ARG(value_len);
+
+  switch (*column) {
+  case 1: /* ipAdEntAddr */
+    value->u32 = netif_ip4_addr(netif)->addr;
+    break;
+  case 2: /* ipAdEntIfIndex */
+    value->u32 = netif_to_num(netif);
+    break;
+  case 3: /* ipAdEntNetMask */
+    value->u32 = netif_ip4_netmask(netif)->addr;
+    break;
+  case 4: /* ipAdEntBcastAddr */
+    /* lwIP oddity, there's no broadcast
+       address in the netif we can rely on */
+    value->u32 = IPADDR_BROADCAST & 1;
+    break;
+  case 5: /* ipAdEntReasmMaxSize */
+#if IP_REASSEMBLY
+    /* @todo The theoretical maximum is IP_REASS_MAX_PBUFS * size of the pbufs,
+     * but only if receiving one fragmented packet at a time.
+     * The current solution is to calculate for 2 simultaneous packets...
+     */
+    value->u32 = (IP_HLEN + ((IP_REASS_MAX_PBUFS/2) *
+        (PBUF_POOL_BUFSIZE - PBUF_LINK_ENCAPSULATION_HLEN - PBUF_LINK_HLEN - IP_HLEN)));
+#else
+    /** @todo returning MTU would be a bad thing and
+        returning a wild guess like '576' isn't good either */
+    value->u32 = 0;
+#endif
+    break;
+  default:
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  return SNMP_ERR_NOERROR;
+}
+
+static snmp_err_t
+ip_AddrTable_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len)
+{
+  ip4_addr_t ip;
+  struct netif *netif;
+
+  /* check if incoming OID length and if values are in plausible range */
+  if (!snmp_oid_in_range(row_oid, row_oid_len, ip_AddrTable_oid_ranges, LWIP_ARRAYSIZE(ip_AddrTable_oid_ranges))) {
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  /* get IP from incoming OID */
+  snmp_oid_to_ip4(&row_oid[0], &ip); /* we know it succeeds because of oid_in_range check above */
+
+  /* find netif with requested ip */
+  netif = netif_list;
+  while (netif != NULL) {
+    if (ip4_addr_cmp(&ip, netif_ip4_addr(netif))) {
+      /* fill in object properties */
+      return ip_AddrTable_get_cell_value_core(netif, column, value, value_len);
+    }
+
+    netif = netif->next;
+  }
+
+  /* not found */
+  return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+static snmp_err_t
+ip_AddrTable_get_next_cell_instance_and_value(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len)
+{
+  struct netif *netif;
+  struct snmp_next_oid_state state;
+  u32_t result_temp[LWIP_ARRAYSIZE(ip_AddrTable_oid_ranges)];
+
+  /* init struct to search next oid */
+  snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(ip_AddrTable_oid_ranges));
+
+  /* iterate over all possible OIDs to find the next one */
+  netif = netif_list;
+  while (netif != NULL) {
+    u32_t test_oid[LWIP_ARRAYSIZE(ip_AddrTable_oid_ranges)];
+    snmp_ip4_to_oid(netif_ip4_addr(netif), &test_oid[0]);
+
+    /* check generated OID: is it a candidate for the next one? */
+    snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(ip_AddrTable_oid_ranges), netif);
+
+    netif = netif->next;
+  }
+
+  /* did we find a next one? */
+  if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
+    snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
+    /* fill in object properties */
+    return ip_AddrTable_get_cell_value_core((struct netif*)state.reference, column, value, value_len);
+  }
+
+  /* not found */
+  return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+/* --- ipRouteTable --- */
+
+/* list of allowed value ranges for incoming OID */
+static const struct snmp_oid_range ip_RouteTable_oid_ranges[] = {
+  { 0, 0xff }, /* IP A */
+  { 0, 0xff }, /* IP B */
+  { 0, 0xff }, /* IP C */
+  { 0, 0xff }, /* IP D */
+};
+
+static snmp_err_t
+ip_RouteTable_get_cell_value_core(struct netif *netif, u8_t default_route, const u32_t* column, union snmp_variant_value* value, u32_t* value_len)
+{
+  switch (*column) {
+  case 1: /* ipRouteDest */
+    if (default_route) {
+       /* default rte has 0.0.0.0 dest */
+      value->u32 = IP4_ADDR_ANY4->addr;
+    } else {
+      /* netifs have netaddress dest */
+      ip4_addr_t tmp;
+      ip4_addr_get_network(&tmp, netif_ip4_addr(netif), netif_ip4_netmask(netif));
+      value->u32 = tmp.addr;
+    }
+    break;
+  case 2: /* ipRouteIfIndex */
+    value->u32 = netif_to_num(netif);
+    break;
+  case 3: /* ipRouteMetric1 */
+    if (default_route) {
+      value->s32 = 1; /* default */
+    } else {
+      value->s32 = 0; /* normal */
+    }
+    break;
+  case 4: /* ipRouteMetric2 */
+  case 5: /* ipRouteMetric3 */
+  case 6: /* ipRouteMetric4 */
+    value->s32 = -1; /* none */
+    break;
+  case 7: /* ipRouteNextHop */
+    if (default_route) {
+      /* default rte: gateway */
+      value->u32 = netif_ip4_gw(netif)->addr;
+    } else {
+      /* other rtes: netif ip_addr  */
+      value->u32 = netif_ip4_addr(netif)->addr;
+    }
+    break;
+  case 8: /* ipRouteType */
+    if (default_route) {
+      /* default rte is indirect */
+      value->u32 = 4; /* indirect */
+    } else {
+      /* other rtes are direct */
+      value->u32 = 3; /* direct */
+    }
+    break;
+  case 9: /* ipRouteProto */
+    /* locally defined routes */
+    value->u32 = 2; /* local */
+    break;
+  case 10: /* ipRouteAge */
+    /* @todo (sysuptime - timestamp last change) / 100 */
+    value->u32 = 0;
+    break;
+  case 11: /* ipRouteMask */
+    if (default_route) {
+      /* default rte use 0.0.0.0 mask */
+      value->u32 = IP4_ADDR_ANY4->addr;
+    } else {
+      /* other rtes use netmask */
+      value->u32 = netif_ip4_netmask(netif)->addr;
+    }
+    break;
+  case 12: /* ipRouteMetric5 */
+    value->s32 = -1; /* none */
+    break;
+  case 13: /* ipRouteInfo */
+    value->const_ptr = snmp_zero_dot_zero.id;
+    *value_len = snmp_zero_dot_zero.len * sizeof(u32_t);
+    break;
+  default:
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  return SNMP_ERR_NOERROR;
+}
+
+static snmp_err_t
+ip_RouteTable_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len)
+{
+  ip4_addr_t test_ip;
+  struct netif *netif;
+
+  /* check if incoming OID length and if values are in plausible range */
+  if (!snmp_oid_in_range(row_oid, row_oid_len, ip_RouteTable_oid_ranges, LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges))) {
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  /* get IP and port from incoming OID */
+  snmp_oid_to_ip4(&row_oid[0], &test_ip); /* we know it succeeds because of oid_in_range check above */
+
+  /* default route is on default netif */
+  if (ip4_addr_isany_val(test_ip) && (netif_default != NULL)) {
+    /* fill in object properties */
+    return ip_RouteTable_get_cell_value_core(netif_default, 1, column, value, value_len);
+  }
+
+  /* find netif with requested route */
+  netif = netif_list;
+  while (netif != NULL) {
+    ip4_addr_t dst;
+    ip4_addr_get_network(&dst, netif_ip4_addr(netif), netif_ip4_netmask(netif));
+
+    if (ip4_addr_cmp(&dst, &test_ip)) {
+      /* fill in object properties */
+      return ip_RouteTable_get_cell_value_core(netif, 0, column, value, value_len);
+    }
+
+    netif = netif->next;
+  }
+
+  /* not found */
+  return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+static snmp_err_t
+ip_RouteTable_get_next_cell_instance_and_value(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len)
+{
+  struct netif *netif;
+  struct snmp_next_oid_state state;
+  u32_t result_temp[LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges)];
+  u32_t test_oid[LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges)];
+
+  /* init struct to search next oid */
+  snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges));
+
+  /* check default route */
+  if (netif_default != NULL) {
+    snmp_ip4_to_oid(IP4_ADDR_ANY4, &test_oid[0]);
+    snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges), netif_default);
+  }
+
+  /* iterate over all possible OIDs to find the next one */
+  netif = netif_list;
+  while (netif != NULL) {
+    ip4_addr_t dst;
+    ip4_addr_get_network(&dst, netif_ip4_addr(netif), netif_ip4_netmask(netif));
+
+    /* check generated OID: is it a candidate for the next one? */
+    if (!ip4_addr_isany_val(dst)) {
+      snmp_ip4_to_oid(&dst, &test_oid[0]);
+      snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges), netif);
+    }
+
+    netif = netif->next;
+  }
+
+  /* did we find a next one? */
+  if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
+    ip4_addr_t dst;
+    snmp_oid_to_ip4(&result_temp[0], &dst);
+    snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
+    /* fill in object properties */
+    return ip_RouteTable_get_cell_value_core((struct netif*)state.reference, ip4_addr_isany_val(dst), column, value, value_len);
+  } else {
+    /* not found */
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+}
+
+#if LWIP_ARP && LWIP_IPV4
+/* --- ipNetToMediaTable --- */
+
+/* list of allowed value ranges for incoming OID */
+static const struct snmp_oid_range ip_NetToMediaTable_oid_ranges[] = {
+  { 1, 0xff }, /* IfIndex */
+  { 0, 0xff }, /* IP A    */
+  { 0, 0xff }, /* IP B    */
+  { 0, 0xff }, /* IP C    */
+  { 0, 0xff }  /* IP D    */
+};
+
+static snmp_err_t
+ip_NetToMediaTable_get_cell_value_core(u8_t arp_table_index, const u32_t* column, union snmp_variant_value* value, u32_t* value_len)
+{
+  ip4_addr_t *ip;
+  struct netif *netif;
+  struct eth_addr *ethaddr;
+
+  etharp_get_entry(arp_table_index, &ip, &netif, &ethaddr);
+
+  /* value */
+  switch (*column) {
+  case 1: /* atIfIndex / ipNetToMediaIfIndex */
+    value->u32 = netif_to_num(netif);
+    break;
+  case 2: /* atPhysAddress / ipNetToMediaPhysAddress */
+    value->ptr = ethaddr;
+    *value_len = sizeof(*ethaddr);
+    break;
+  case 3: /* atNetAddress / ipNetToMediaNetAddress */
+    value->u32 = ip->addr;
+    break;
+  case 4: /* ipNetToMediaType */
+    value->u32 = 3; /* dynamic*/
+    break;
+  default:
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  return SNMP_ERR_NOERROR;
+}
+
+static snmp_err_t
+ip_NetToMediaTable_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len)
+{
+  ip4_addr_t ip_in;
+  u8_t netif_index;
+  u8_t i;
+
+  /* check if incoming OID length and if values are in plausible range */
+  if (!snmp_oid_in_range(row_oid, row_oid_len, ip_NetToMediaTable_oid_ranges, LWIP_ARRAYSIZE(ip_NetToMediaTable_oid_ranges))) {
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  /* get IP from incoming OID */
+  netif_index = (u8_t)row_oid[0];
+  snmp_oid_to_ip4(&row_oid[1], &ip_in); /* we know it succeeds because of oid_in_range check above */
+
+  /* find requested entry */
+  for (i=0; i<ARP_TABLE_SIZE; i++) {
+    ip4_addr_t *ip;
+    struct netif *netif;
+    struct eth_addr *ethaddr;
+
+    if (etharp_get_entry(i, &ip, &netif, &ethaddr)) {
+      if ((netif_index == netif_to_num(netif)) && ip4_addr_cmp(&ip_in, ip)) {
+        /* fill in object properties */
+        return ip_NetToMediaTable_get_cell_value_core(i, column, value, value_len);
+      }
+    }
+  }
+
+  /* not found */
+  return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+static snmp_err_t
+ip_NetToMediaTable_get_next_cell_instance_and_value(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len)
+{
+  u8_t i;
+  struct snmp_next_oid_state state;
+  u32_t result_temp[LWIP_ARRAYSIZE(ip_NetToMediaTable_oid_ranges)];
+
+  /* init struct to search next oid */
+  snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(ip_NetToMediaTable_oid_ranges));
+
+  /* iterate over all possible OIDs to find the next one */
+  for (i=0; i<ARP_TABLE_SIZE; i++) {
+    ip4_addr_t *ip;
+    struct netif *netif;
+    struct eth_addr *ethaddr;
+
+    if (etharp_get_entry(i, &ip, &netif, &ethaddr)) {
+      u32_t test_oid[LWIP_ARRAYSIZE(ip_NetToMediaTable_oid_ranges)];
+
+      test_oid[0] = netif_to_num(netif);
+      snmp_ip4_to_oid(ip, &test_oid[1]);
+
+      /* check generated OID: is it a candidate for the next one? */
+      snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(ip_NetToMediaTable_oid_ranges), LWIP_PTR_NUMERIC_CAST(void*, i));
+    }
+  }
+
+  /* did we find a next one? */
+  if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
+    snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
+    /* fill in object properties */
+    return ip_NetToMediaTable_get_cell_value_core(LWIP_PTR_NUMERIC_CAST(u8_t, state.reference), column, value, value_len);
+  }
+
+  /* not found */
+  return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+#endif /* LWIP_ARP && LWIP_IPV4 */
+
+static const struct snmp_scalar_node ip_Forwarding      = SNMP_SCALAR_CREATE_NODE(1, SNMP_NODE_INSTANCE_READ_WRITE, SNMP_ASN1_TYPE_INTEGER, ip_get_value, ip_set_test, ip_set_value);
+static const struct snmp_scalar_node ip_DefaultTTL      = SNMP_SCALAR_CREATE_NODE(2, SNMP_NODE_INSTANCE_READ_WRITE, SNMP_ASN1_TYPE_INTEGER, ip_get_value, ip_set_test, ip_set_value);
+static const struct snmp_scalar_node ip_InReceives      = SNMP_SCALAR_CREATE_NODE_READONLY(3, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_InHdrErrors     = SNMP_SCALAR_CREATE_NODE_READONLY(4, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_InAddrErrors    = SNMP_SCALAR_CREATE_NODE_READONLY(5, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_ForwDatagrams   = SNMP_SCALAR_CREATE_NODE_READONLY(6, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_InUnknownProtos = SNMP_SCALAR_CREATE_NODE_READONLY(7, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_InDiscards      = SNMP_SCALAR_CREATE_NODE_READONLY(8, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_InDelivers      = SNMP_SCALAR_CREATE_NODE_READONLY(9, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_OutRequests     = SNMP_SCALAR_CREATE_NODE_READONLY(10, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_OutDiscards     = SNMP_SCALAR_CREATE_NODE_READONLY(11, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_OutNoRoutes     = SNMP_SCALAR_CREATE_NODE_READONLY(12, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_ReasmTimeout    = SNMP_SCALAR_CREATE_NODE_READONLY(13, SNMP_ASN1_TYPE_INTEGER, ip_get_value);
+static const struct snmp_scalar_node ip_ReasmReqds      = SNMP_SCALAR_CREATE_NODE_READONLY(14, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_ReasmOKs        = SNMP_SCALAR_CREATE_NODE_READONLY(15, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_ReasmFails      = SNMP_SCALAR_CREATE_NODE_READONLY(16, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_FragOKs         = SNMP_SCALAR_CREATE_NODE_READONLY(17, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_FragFails       = SNMP_SCALAR_CREATE_NODE_READONLY(18, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_FragCreates     = SNMP_SCALAR_CREATE_NODE_READONLY(19, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_RoutingDiscards = SNMP_SCALAR_CREATE_NODE_READONLY(23, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+
+static const struct snmp_table_simple_col_def ip_AddrTable_columns[] = {
+  { 1, SNMP_ASN1_TYPE_IPADDR,  SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipAdEntAddr */
+  { 2, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipAdEntIfIndex */
+  { 3, SNMP_ASN1_TYPE_IPADDR,  SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipAdEntNetMask */
+  { 4, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipAdEntBcastAddr */
+  { 5, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }  /* ipAdEntReasmMaxSize */
+};
+
+static const struct snmp_table_simple_node ip_AddrTable = SNMP_TABLE_CREATE_SIMPLE(20, ip_AddrTable_columns, ip_AddrTable_get_cell_value, ip_AddrTable_get_next_cell_instance_and_value);
+
+static const struct snmp_table_simple_col_def ip_RouteTable_columns[] = {
+  {  1, SNMP_ASN1_TYPE_IPADDR,    SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteDest */
+  {  2, SNMP_ASN1_TYPE_INTEGER,   SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteIfIndex */
+  {  3, SNMP_ASN1_TYPE_INTEGER,   SNMP_VARIANT_VALUE_TYPE_S32 }, /* ipRouteMetric1 */
+  {  4, SNMP_ASN1_TYPE_INTEGER,   SNMP_VARIANT_VALUE_TYPE_S32 }, /* ipRouteMetric2 */
+  {  5, SNMP_ASN1_TYPE_INTEGER,   SNMP_VARIANT_VALUE_TYPE_S32 }, /* ipRouteMetric3 */
+  {  6, SNMP_ASN1_TYPE_INTEGER,   SNMP_VARIANT_VALUE_TYPE_S32 }, /* ipRouteMetric4 */
+  {  7, SNMP_ASN1_TYPE_IPADDR,    SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteNextHop */
+  {  8, SNMP_ASN1_TYPE_INTEGER,   SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteType */
+  {  9, SNMP_ASN1_TYPE_INTEGER,   SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteProto */
+  { 10, SNMP_ASN1_TYPE_INTEGER,   SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteAge */
+  { 11, SNMP_ASN1_TYPE_IPADDR,    SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteMask */
+  { 12, SNMP_ASN1_TYPE_INTEGER,   SNMP_VARIANT_VALUE_TYPE_S32 }, /* ipRouteMetric5 */
+  { 13, SNMP_ASN1_TYPE_OBJECT_ID, SNMP_VARIANT_VALUE_TYPE_PTR }  /* ipRouteInfo */
+};
+
+static const struct snmp_table_simple_node ip_RouteTable = SNMP_TABLE_CREATE_SIMPLE(21, ip_RouteTable_columns, ip_RouteTable_get_cell_value, ip_RouteTable_get_next_cell_instance_and_value);
+#endif /* LWIP_IPV4 */
+
+#if LWIP_ARP && LWIP_IPV4
+static const struct snmp_table_simple_col_def ip_NetToMediaTable_columns[] = {
+  {  1, SNMP_ASN1_TYPE_INTEGER,      SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipNetToMediaIfIndex */
+  {  2, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_VARIANT_VALUE_TYPE_PTR }, /* ipNetToMediaPhysAddress */
+  {  3, SNMP_ASN1_TYPE_IPADDR,       SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipNetToMediaNetAddress */
+  {  4, SNMP_ASN1_TYPE_INTEGER,      SNMP_VARIANT_VALUE_TYPE_U32 }  /* ipNetToMediaType */
+};
+
+static const struct snmp_table_simple_node ip_NetToMediaTable = SNMP_TABLE_CREATE_SIMPLE(22, ip_NetToMediaTable_columns, ip_NetToMediaTable_get_cell_value, ip_NetToMediaTable_get_next_cell_instance_and_value);
+#endif /* LWIP_ARP && LWIP_IPV4 */
+
+#if LWIP_IPV4
+/* the following nodes access variables in LWIP stack from SNMP worker thread and must therefore be synced to LWIP (TCPIP) thread */
+CREATE_LWIP_SYNC_NODE( 1, ip_Forwarding)
+CREATE_LWIP_SYNC_NODE( 2, ip_DefaultTTL)
+CREATE_LWIP_SYNC_NODE( 3, ip_InReceives)
+CREATE_LWIP_SYNC_NODE( 4, ip_InHdrErrors)
+CREATE_LWIP_SYNC_NODE( 5, ip_InAddrErrors)
+CREATE_LWIP_SYNC_NODE( 6, ip_ForwDatagrams)
+CREATE_LWIP_SYNC_NODE( 7, ip_InUnknownProtos)
+CREATE_LWIP_SYNC_NODE( 8, ip_InDiscards)
+CREATE_LWIP_SYNC_NODE( 9, ip_InDelivers)
+CREATE_LWIP_SYNC_NODE(10, ip_OutRequests)
+CREATE_LWIP_SYNC_NODE(11, ip_OutDiscards)
+CREATE_LWIP_SYNC_NODE(12, ip_OutNoRoutes)
+CREATE_LWIP_SYNC_NODE(13, ip_ReasmTimeout)
+CREATE_LWIP_SYNC_NODE(14, ip_ReasmReqds)
+CREATE_LWIP_SYNC_NODE(15, ip_ReasmOKs)
+CREATE_LWIP_SYNC_NODE(15, ip_ReasmFails)
+CREATE_LWIP_SYNC_NODE(17, ip_FragOKs)
+CREATE_LWIP_SYNC_NODE(18, ip_FragFails)
+CREATE_LWIP_SYNC_NODE(19, ip_FragCreates)
+CREATE_LWIP_SYNC_NODE(20, ip_AddrTable)
+CREATE_LWIP_SYNC_NODE(21, ip_RouteTable)
+#if LWIP_ARP
+CREATE_LWIP_SYNC_NODE(22, ip_NetToMediaTable)
+#endif /* LWIP_ARP */
+CREATE_LWIP_SYNC_NODE(23, ip_RoutingDiscards)
+
+static const struct snmp_node* const ip_nodes[] = {
+  &SYNC_NODE_NAME(ip_Forwarding).node.node,
+  &SYNC_NODE_NAME(ip_DefaultTTL).node.node,
+  &SYNC_NODE_NAME(ip_InReceives).node.node,
+  &SYNC_NODE_NAME(ip_InHdrErrors).node.node,
+  &SYNC_NODE_NAME(ip_InAddrErrors).node.node,
+  &SYNC_NODE_NAME(ip_ForwDatagrams).node.node,
+  &SYNC_NODE_NAME(ip_InUnknownProtos).node.node,
+  &SYNC_NODE_NAME(ip_InDiscards).node.node,
+  &SYNC_NODE_NAME(ip_InDelivers).node.node,
+  &SYNC_NODE_NAME(ip_OutRequests).node.node,
+  &SYNC_NODE_NAME(ip_OutDiscards).node.node,
+  &SYNC_NODE_NAME(ip_OutNoRoutes).node.node,
+  &SYNC_NODE_NAME(ip_ReasmTimeout).node.node,
+  &SYNC_NODE_NAME(ip_ReasmReqds).node.node,
+  &SYNC_NODE_NAME(ip_ReasmOKs).node.node,
+  &SYNC_NODE_NAME(ip_ReasmFails).node.node,
+  &SYNC_NODE_NAME(ip_FragOKs).node.node,
+  &SYNC_NODE_NAME(ip_FragFails).node.node,
+  &SYNC_NODE_NAME(ip_FragCreates).node.node,
+  &SYNC_NODE_NAME(ip_AddrTable).node.node,
+  &SYNC_NODE_NAME(ip_RouteTable).node.node,
+#if LWIP_ARP
+  &SYNC_NODE_NAME(ip_NetToMediaTable).node.node,
+#endif /* LWIP_ARP */
+  &SYNC_NODE_NAME(ip_RoutingDiscards).node.node
+};
+
+const struct snmp_tree_node snmp_mib2_ip_root = SNMP_CREATE_TREE_NODE(4, ip_nodes);
+#endif /* LWIP_IPV4 */
+
+/* --- at .1.3.6.1.2.1.3 ----------------------------------------------------- */
+
+#if LWIP_ARP && LWIP_IPV4
+/* at node table is a subset of ip_nettomedia table (same rows but less columns) */
+static const struct snmp_table_simple_col_def at_Table_columns[] = {
+  { 1, SNMP_ASN1_TYPE_INTEGER,      SNMP_VARIANT_VALUE_TYPE_U32 }, /* atIfIndex */
+  { 2, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_VARIANT_VALUE_TYPE_PTR }, /* atPhysAddress */
+  { 3, SNMP_ASN1_TYPE_IPADDR,       SNMP_VARIANT_VALUE_TYPE_U32 }  /* atNetAddress */
+};
+
+static const struct snmp_table_simple_node at_Table = SNMP_TABLE_CREATE_SIMPLE(1, at_Table_columns, ip_NetToMediaTable_get_cell_value, ip_NetToMediaTable_get_next_cell_instance_and_value);
+
+/* the following nodes access variables in LWIP stack from SNMP worker thread and must therefore be synced to LWIP (TCPIP) thread */
+CREATE_LWIP_SYNC_NODE(1, at_Table)
+
+static const struct snmp_node* const at_nodes[] = {
+  &SYNC_NODE_NAME(at_Table).node.node
+};
+
+const struct snmp_tree_node snmp_mib2_at_root = SNMP_CREATE_TREE_NODE(3, at_nodes);
+#endif /* LWIP_ARP && LWIP_IPV4 */
+
+#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 */

+ 227 - 227
libs/thirdparty/lwip_2.1.2/src/apps/snmp/snmp_mib2_snmp.c → libs/thirdparty/LwIP/src/apps/snmp/snmp_mib2_snmp.c

@@ -1,227 +1,227 @@
-/**
- * @file
- * Management Information Base II (RFC1213) SNMP objects and functions.
- */
-
-/*
- * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * Author: Dirk Ziegelmeier <dziegel@gmx.de>
- *         Christiaan Simons <christiaan.simons@axon.tv>
- */
-
-#include "lwip/snmp.h"
-#include "lwip/apps/snmp.h"
-#include "lwip/apps/snmp_core.h"
-#include "lwip/apps/snmp_mib2.h"
-#include "lwip/apps/snmp_scalar.h"
-
-#if LWIP_SNMP && SNMP_LWIP_MIB2
-
-#define MIB2_AUTH_TRAPS_ENABLED  1
-#define MIB2_AUTH_TRAPS_DISABLED 2
-
-/* --- snmp .1.3.6.1.2.1.11 ----------------------------------------------------- */
-static s16_t
-snmp_get_value(const struct snmp_scalar_array_node_def *node, void *value)
-{
-  u32_t *uint_ptr = (u32_t *)value;
-  switch (node->oid) {
-    case 1: /* snmpInPkts */
-      *uint_ptr = snmp_stats.inpkts;
-      break;
-    case 2: /* snmpOutPkts */
-      *uint_ptr = snmp_stats.outpkts;
-      break;
-    case 3: /* snmpInBadVersions */
-      *uint_ptr = snmp_stats.inbadversions;
-      break;
-    case 4: /* snmpInBadCommunityNames */
-      *uint_ptr = snmp_stats.inbadcommunitynames;
-      break;
-    case 5: /* snmpInBadCommunityUses */
-      *uint_ptr = snmp_stats.inbadcommunityuses;
-      break;
-    case 6: /* snmpInASNParseErrs */
-      *uint_ptr = snmp_stats.inasnparseerrs;
-      break;
-    case 8: /* snmpInTooBigs */
-      *uint_ptr = snmp_stats.intoobigs;
-      break;
-    case 9: /* snmpInNoSuchNames */
-      *uint_ptr = snmp_stats.innosuchnames;
-      break;
-    case 10: /* snmpInBadValues */
-      *uint_ptr = snmp_stats.inbadvalues;
-      break;
-    case 11: /* snmpInReadOnlys */
-      *uint_ptr = snmp_stats.inreadonlys;
-      break;
-    case 12: /* snmpInGenErrs */
-      *uint_ptr = snmp_stats.ingenerrs;
-      break;
-    case 13: /* snmpInTotalReqVars */
-      *uint_ptr = snmp_stats.intotalreqvars;
-      break;
-    case 14: /* snmpInTotalSetVars */
-      *uint_ptr = snmp_stats.intotalsetvars;
-      break;
-    case 15: /* snmpInGetRequests */
-      *uint_ptr = snmp_stats.ingetrequests;
-      break;
-    case 16: /* snmpInGetNexts */
-      *uint_ptr = snmp_stats.ingetnexts;
-      break;
-    case 17: /* snmpInSetRequests */
-      *uint_ptr = snmp_stats.insetrequests;
-      break;
-    case 18: /* snmpInGetResponses */
-      *uint_ptr = snmp_stats.ingetresponses;
-      break;
-    case 19: /* snmpInTraps */
-      *uint_ptr = snmp_stats.intraps;
-      break;
-    case 20: /* snmpOutTooBigs */
-      *uint_ptr = snmp_stats.outtoobigs;
-      break;
-    case 21: /* snmpOutNoSuchNames */
-      *uint_ptr = snmp_stats.outnosuchnames;
-      break;
-    case 22: /* snmpOutBadValues */
-      *uint_ptr = snmp_stats.outbadvalues;
-      break;
-    case 24: /* snmpOutGenErrs */
-      *uint_ptr = snmp_stats.outgenerrs;
-      break;
-    case 25: /* snmpOutGetRequests */
-      *uint_ptr = snmp_stats.outgetrequests;
-      break;
-    case 26: /* snmpOutGetNexts */
-      *uint_ptr = snmp_stats.outgetnexts;
-      break;
-    case 27: /* snmpOutSetRequests */
-      *uint_ptr = snmp_stats.outsetrequests;
-      break;
-    case 28: /* snmpOutGetResponses */
-      *uint_ptr = snmp_stats.outgetresponses;
-      break;
-    case 29: /* snmpOutTraps */
-      *uint_ptr = snmp_stats.outtraps;
-      break;
-    case 30: /* snmpEnableAuthenTraps */
-      if (snmp_get_auth_traps_enabled() == SNMP_AUTH_TRAPS_DISABLED) {
-        *uint_ptr = MIB2_AUTH_TRAPS_DISABLED;
-      } else {
-        *uint_ptr = MIB2_AUTH_TRAPS_ENABLED;
-      }
-      break;
-    case 31: /* snmpSilentDrops */
-      *uint_ptr = 0; /* not supported */
-      break;
-    case 32: /* snmpProxyDrops */
-      *uint_ptr = 0; /* not supported */
-      break;
-    default:
-      LWIP_DEBUGF(SNMP_MIB_DEBUG, ("snmp_get_value(): unknown id: %"S32_F"\n", node->oid));
-      return 0;
-  }
-
-  return sizeof(*uint_ptr);
-}
-
-static snmp_err_t
-snmp_set_test(const struct snmp_scalar_array_node_def *node, u16_t len, void *value)
-{
-  snmp_err_t ret = SNMP_ERR_WRONGVALUE;
-  LWIP_UNUSED_ARG(len);
-
-  if (node->oid == 30) {
-    /* snmpEnableAuthenTraps */
-    s32_t *sint_ptr = (s32_t *)value;
-
-    /* we should have writable non-volatile mem here */
-    if ((*sint_ptr == MIB2_AUTH_TRAPS_DISABLED) || (*sint_ptr == MIB2_AUTH_TRAPS_ENABLED)) {
-      ret = SNMP_ERR_NOERROR;
-    }
-  }
-  return ret;
-}
-
-static snmp_err_t
-snmp_set_value(const struct snmp_scalar_array_node_def *node, u16_t len, void *value)
-{
-  LWIP_UNUSED_ARG(len);
-
-  if (node->oid == 30) {
-    /* snmpEnableAuthenTraps */
-    s32_t *sint_ptr = (s32_t *)value;
-    if (*sint_ptr == MIB2_AUTH_TRAPS_DISABLED) {
-      snmp_set_auth_traps_enabled(SNMP_AUTH_TRAPS_DISABLED);
-    } else {
-      snmp_set_auth_traps_enabled(SNMP_AUTH_TRAPS_ENABLED);
-    }
-  }
-
-  return SNMP_ERR_NOERROR;
-}
-
-/* the following nodes access variables in SNMP stack (snmp_stats) from SNMP worker thread -> OK, no sync needed */
-static const struct snmp_scalar_array_node_def snmp_nodes[] = {
-  { 1, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInPkts */
-  { 2, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpOutPkts */
-  { 3, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInBadVersions */
-  { 4, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInBadCommunityNames */
-  { 5, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInBadCommunityUses */
-  { 6, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInASNParseErrs */
-  { 8, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInTooBigs */
-  { 9, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInNoSuchNames */
-  {10, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInBadValues */
-  {11, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInReadOnlys */
-  {12, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInGenErrs */
-  {13, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInTotalReqVars */
-  {14, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInTotalSetVars */
-  {15, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInGetRequests */
-  {16, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInGetNexts */
-  {17, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInSetRequests */
-  {18, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInGetResponses */
-  {19, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInTraps */
-  {20, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpOutTooBigs */
-  {21, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpOutNoSuchNames */
-  {22, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpOutBadValues */
-  {24, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpOutGenErrs */
-  {25, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpOutGetRequests */
-  {26, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpOutGetNexts */
-  {27, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpOutSetRequests */
-  {28, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpOutGetResponses */
-  {29, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpOutTraps */
-  {30, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_WRITE}, /* snmpEnableAuthenTraps */
-  {31, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpSilentDrops */
-  {32, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}   /* snmpProxyDrops */
-};
-
-const struct snmp_scalar_array_node snmp_mib2_snmp_root = SNMP_SCALAR_CREATE_ARRAY_NODE(11, snmp_nodes, snmp_get_value, snmp_set_test, snmp_set_value);
-
-#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 */
+/**
+ * @file
+ * Management Information Base II (RFC1213) SNMP objects and functions.
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Dirk Ziegelmeier <dziegel@gmx.de>
+ *         Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#include "lwip/snmp.h"
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "lwip/apps/snmp_mib2.h"
+#include "lwip/apps/snmp_scalar.h"
+
+#if LWIP_SNMP && SNMP_LWIP_MIB2
+
+#define MIB2_AUTH_TRAPS_ENABLED  1
+#define MIB2_AUTH_TRAPS_DISABLED 2
+
+/* --- snmp .1.3.6.1.2.1.11 ----------------------------------------------------- */
+static s16_t
+snmp_get_value(const struct snmp_scalar_array_node_def *node, void *value)
+{
+  u32_t *uint_ptr = (u32_t*)value;
+  switch (node->oid) {
+  case 1: /* snmpInPkts */
+    *uint_ptr = snmp_stats.inpkts;
+    break;
+  case 2: /* snmpOutPkts */
+    *uint_ptr = snmp_stats.outpkts;
+    break;
+  case 3: /* snmpInBadVersions */
+    *uint_ptr = snmp_stats.inbadversions;
+    break;
+  case 4: /* snmpInBadCommunityNames */
+    *uint_ptr = snmp_stats.inbadcommunitynames;
+    break;
+  case 5: /* snmpInBadCommunityUses */
+    *uint_ptr = snmp_stats.inbadcommunityuses;
+    break;
+  case 6: /* snmpInASNParseErrs */
+    *uint_ptr = snmp_stats.inasnparseerrs;
+    break;
+  case 8: /* snmpInTooBigs */
+    *uint_ptr = snmp_stats.intoobigs;
+    break;
+  case 9: /* snmpInNoSuchNames */
+    *uint_ptr = snmp_stats.innosuchnames;
+    break;
+  case 10: /* snmpInBadValues */
+    *uint_ptr = snmp_stats.inbadvalues;
+    break;
+  case 11: /* snmpInReadOnlys */
+    *uint_ptr = snmp_stats.inreadonlys;
+    break;
+  case 12: /* snmpInGenErrs */
+    *uint_ptr = snmp_stats.ingenerrs;
+    break;
+  case 13: /* snmpInTotalReqVars */
+    *uint_ptr = snmp_stats.intotalreqvars;
+    break;
+  case 14: /* snmpInTotalSetVars */
+    *uint_ptr = snmp_stats.intotalsetvars;
+    break;
+  case 15: /* snmpInGetRequests */
+    *uint_ptr = snmp_stats.ingetrequests;
+    break;
+  case 16: /* snmpInGetNexts */
+    *uint_ptr = snmp_stats.ingetnexts;
+    break;
+  case 17: /* snmpInSetRequests */
+    *uint_ptr = snmp_stats.insetrequests;
+    break;
+  case 18: /* snmpInGetResponses */
+    *uint_ptr = snmp_stats.ingetresponses;
+    break;
+  case 19: /* snmpInTraps */
+    *uint_ptr = snmp_stats.intraps;
+    break;
+  case 20: /* snmpOutTooBigs */
+    *uint_ptr = snmp_stats.outtoobigs;
+    break;
+  case 21: /* snmpOutNoSuchNames */
+    *uint_ptr = snmp_stats.outnosuchnames;
+    break;
+  case 22: /* snmpOutBadValues */
+    *uint_ptr = snmp_stats.outbadvalues;
+    break;
+  case 24: /* snmpOutGenErrs */
+    *uint_ptr = snmp_stats.outgenerrs;
+    break;
+  case 25: /* snmpOutGetRequests */
+    *uint_ptr = snmp_stats.outgetrequests;
+    break;
+  case 26: /* snmpOutGetNexts */
+    *uint_ptr = snmp_stats.outgetnexts;
+    break;
+  case 27: /* snmpOutSetRequests */
+    *uint_ptr = snmp_stats.outsetrequests;
+    break;
+  case 28: /* snmpOutGetResponses */
+    *uint_ptr = snmp_stats.outgetresponses;
+    break;
+  case 29: /* snmpOutTraps */
+    *uint_ptr = snmp_stats.outtraps;
+    break;
+  case 30: /* snmpEnableAuthenTraps */
+    if (snmp_get_auth_traps_enabled() == SNMP_AUTH_TRAPS_DISABLED) {
+      *uint_ptr = MIB2_AUTH_TRAPS_DISABLED;
+    } else {
+      *uint_ptr = MIB2_AUTH_TRAPS_ENABLED;
+    }
+    break;
+  case 31: /* snmpSilentDrops */
+    *uint_ptr = 0; /* not supported */
+    break;
+  case 32: /* snmpProxyDrops */
+    *uint_ptr = 0; /* not supported */
+    break;
+  default:
+    LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_get_value(): unknown id: %"S32_F"\n", node->oid));
+    return 0;
+  }
+
+  return sizeof(*uint_ptr);
+}
+
+static snmp_err_t
+snmp_set_test(const struct snmp_scalar_array_node_def *node, u16_t len, void *value)
+{
+  snmp_err_t ret = SNMP_ERR_WRONGVALUE;
+  LWIP_UNUSED_ARG(len);
+
+  if (node->oid == 30) {
+    /* snmpEnableAuthenTraps */
+    s32_t *sint_ptr = (s32_t*)value;
+
+    /* we should have writable non-volatile mem here */
+    if ((*sint_ptr == MIB2_AUTH_TRAPS_DISABLED) || (*sint_ptr == MIB2_AUTH_TRAPS_ENABLED)) {
+      ret = SNMP_ERR_NOERROR;
+    }
+  }
+  return ret;
+}
+
+static snmp_err_t
+snmp_set_value(const struct snmp_scalar_array_node_def *node, u16_t len, void *value)
+{
+  LWIP_UNUSED_ARG(len);
+
+  if (node->oid == 30) {
+    /* snmpEnableAuthenTraps */
+    s32_t *sint_ptr = (s32_t*)value;
+    if (*sint_ptr == MIB2_AUTH_TRAPS_DISABLED) {
+      snmp_set_auth_traps_enabled(SNMP_AUTH_TRAPS_DISABLED);
+    } else {
+      snmp_set_auth_traps_enabled(SNMP_AUTH_TRAPS_ENABLED);
+    }
+  }
+
+  return SNMP_ERR_NOERROR;
+}
+
+/* the following nodes access variables in SNMP stack (snmp_stats) from SNMP worker thread -> OK, no sync needed */
+static const struct snmp_scalar_array_node_def snmp_nodes[] = {
+  { 1, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInPkts */
+  { 2, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpOutPkts */
+  { 3, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInBadVersions */
+  { 4, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInBadCommunityNames */
+  { 5, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInBadCommunityUses */
+  { 6, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInASNParseErrs */
+  { 8, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInTooBigs */
+  { 9, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInNoSuchNames */
+  {10, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInBadValues */
+  {11, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInReadOnlys */
+  {12, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInGenErrs */
+  {13, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInTotalReqVars */
+  {14, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInTotalSetVars */
+  {15, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInGetRequests */
+  {16, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInGetNexts */
+  {17, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInSetRequests */
+  {18, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInGetResponses */
+  {19, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpInTraps */
+  {20, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpOutTooBigs */
+  {21, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpOutNoSuchNames */
+  {22, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpOutBadValues */
+  {24, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpOutGenErrs */
+  {25, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpOutGetRequests */
+  {26, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpOutGetNexts */
+  {27, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpOutSetRequests */
+  {28, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpOutGetResponses */
+  {29, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpOutTraps */
+  {30, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_WRITE}, /* snmpEnableAuthenTraps */
+  {31, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},  /* snmpSilentDrops */
+  {32, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}   /* snmpProxyDrops */
+};
+
+const struct snmp_scalar_array_node snmp_mib2_snmp_root = SNMP_SCALAR_CREATE_ARRAY_NODE(11, snmp_nodes, snmp_get_value, snmp_set_test, snmp_set_value);
+
+#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 */

+ 377 - 380
libs/thirdparty/lwip_2.1.2/src/apps/snmp/snmp_mib2_system.c → libs/thirdparty/LwIP/src/apps/snmp/snmp_mib2_system.c

@@ -1,380 +1,377 @@
-/**
- * @file
- * Management Information Base II (RFC1213) SYSTEM objects and functions.
- */
-
-/*
- * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * Author: Dirk Ziegelmeier <dziegel@gmx.de>
- *         Christiaan Simons <christiaan.simons@axon.tv>
- */
-
-#include "lwip/snmp.h"
-#include "lwip/apps/snmp.h"
-#include "lwip/apps/snmp_core.h"
-#include "lwip/apps/snmp_mib2.h"
-#include "lwip/apps/snmp_table.h"
-#include "lwip/apps/snmp_scalar.h"
-#include "lwip/sys.h"
-
-#include <string.h>
-
-#if LWIP_SNMP && SNMP_LWIP_MIB2
-
-#if SNMP_USE_NETCONN
-#define SYNC_NODE_NAME(node_name) node_name ## _synced
-#define CREATE_LWIP_SYNC_NODE(oid, node_name) \
-   static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks);
-#else
-#define SYNC_NODE_NAME(node_name) node_name
-#define CREATE_LWIP_SYNC_NODE(oid, node_name)
-#endif
-
-/* --- system .1.3.6.1.2.1.1 ----------------------------------------------------- */
-
-/** mib-2.system.sysDescr */
-static const u8_t   sysdescr_default[] = SNMP_LWIP_MIB2_SYSDESC;
-static const u8_t  *sysdescr           = sysdescr_default;
-static const u16_t *sysdescr_len       = NULL; /* use strlen for determining len */
-
-/** mib-2.system.sysContact */
-static const u8_t   syscontact_default[]     = SNMP_LWIP_MIB2_SYSCONTACT;
-static const u8_t  *syscontact               = syscontact_default;
-static const u16_t *syscontact_len           = NULL; /* use strlen for determining len */
-static u8_t        *syscontact_wr            = NULL; /* if writable, points to the same buffer as syscontact (required for correct constness) */
-static u16_t       *syscontact_wr_len        = NULL; /* if writable, points to the same buffer as syscontact_len (required for correct constness) */
-static u16_t        syscontact_bufsize       = 0;    /* 0=not writable */
-
-/** mib-2.system.sysName */
-static const u8_t   sysname_default[]        = SNMP_LWIP_MIB2_SYSNAME;
-static const u8_t  *sysname                  = sysname_default;
-static const u16_t *sysname_len              = NULL; /* use strlen for determining len */
-static u8_t        *sysname_wr               = NULL; /* if writable, points to the same buffer as sysname (required for correct constness) */
-static u16_t       *sysname_wr_len           = NULL; /* if writable, points to the same buffer as sysname_len (required for correct constness) */
-static u16_t        sysname_bufsize          = 0;    /* 0=not writable */
-
-/** mib-2.system.sysLocation */
-static const u8_t   syslocation_default[]    = SNMP_LWIP_MIB2_SYSLOCATION;
-static const u8_t  *syslocation              = syslocation_default;
-static const u16_t *syslocation_len           = NULL; /* use strlen for determining len */
-static u8_t        *syslocation_wr            = NULL; /* if writable, points to the same buffer as syslocation (required for correct constness) */
-static u16_t       *syslocation_wr_len        = NULL; /* if writable, points to the same buffer as syslocation_len (required for correct constness) */
-static u16_t        syslocation_bufsize       = 0;    /* 0=not writable */
-
-/**
- * @ingroup snmp_mib2
- * Initializes sysDescr pointers.
- *
- * @param str if non-NULL then copy str pointer
- * @param len points to string length, excluding zero terminator
- */
-void
-snmp_mib2_set_sysdescr(const u8_t *str, const u16_t *len)
-{
-  if (str != NULL) {
-    sysdescr     = str;
-    sysdescr_len = len;
-  }
-  printf("%s\n", sysdescr);
-}
-
-/**
- * @ingroup snmp_mib2
- * Initializes sysContact pointers
- *
- * @param ocstr if non-NULL then copy str pointer
- * @param ocstrlen points to string length, excluding zero terminator.
- *        if set to NULL it is assumed that ocstr is NULL-terminated.
- * @param bufsize size of the buffer in bytes.
- *        (this is required because the buffer can be overwritten by snmp-set)
- *        if ocstrlen is NULL buffer needs space for terminating 0 byte.
- *        otherwise complete buffer is used for string.
- *        if bufsize is set to 0, the value is regarded as read-only.
- */
-void
-snmp_mib2_set_syscontact(u8_t *ocstr, u16_t *ocstrlen, u16_t bufsize)
-{
-  if (ocstr != NULL) {
-    syscontact         = ocstr;
-    syscontact_wr      = ocstr;
-    syscontact_len     = ocstrlen;
-    syscontact_wr_len  = ocstrlen;
-    syscontact_bufsize = bufsize;
-  }
-  printf("%s\n", syscontact);
-}
-
-/**
- * @ingroup snmp_mib2
- * see \ref snmp_mib2_set_syscontact but set pointer to readonly memory
- */
-void
-snmp_mib2_set_syscontact_readonly(const u8_t *ocstr, const u16_t *ocstrlen)
-{
-  if (ocstr != NULL) {
-    syscontact         = ocstr;
-    syscontact_len     = ocstrlen;
-    syscontact_wr      = NULL;
-    syscontact_wr_len  = NULL;
-    syscontact_bufsize = 0;
-  }
-}
-
-
-/**
- * @ingroup snmp_mib2
- * Initializes sysName pointers
- *
- * @param ocstr if non-NULL then copy str pointer
- * @param ocstrlen points to string length, excluding zero terminator.
- *        if set to NULL it is assumed that ocstr is NULL-terminated.
- * @param bufsize size of the buffer in bytes.
- *        (this is required because the buffer can be overwritten by snmp-set)
- *        if ocstrlen is NULL buffer needs space for terminating 0 byte.
- *        otherwise complete buffer is used for string.
- *        if bufsize is set to 0, the value is regarded as read-only.
- */
-void
-snmp_mib2_set_sysname(u8_t *ocstr, u16_t *ocstrlen, u16_t bufsize)
-{
-  if (ocstr != NULL) {
-    sysname         = ocstr;
-    sysname_wr      = ocstr;
-    sysname_len     = ocstrlen;
-    sysname_wr_len  = ocstrlen;
-    sysname_bufsize = bufsize;
-  }
-  printf("%s\n", sysname);
-}
-
-/**
- * @ingroup snmp_mib2
- * see \ref snmp_mib2_set_sysname but set pointer to readonly memory
- */
-void
-snmp_mib2_set_sysname_readonly(const u8_t *ocstr, const u16_t *ocstrlen)
-{
-  if (ocstr != NULL) {
-    sysname         = ocstr;
-    sysname_len     = ocstrlen;
-    sysname_wr      = NULL;
-    sysname_wr_len  = NULL;
-    sysname_bufsize = 0;
-  }
-}
-
-/**
- * @ingroup snmp_mib2
- * Initializes sysLocation pointers
- *
- * @param ocstr if non-NULL then copy str pointer
- * @param ocstrlen points to string length, excluding zero terminator.
- *        if set to NULL it is assumed that ocstr is NULL-terminated.
- * @param bufsize size of the buffer in bytes.
- *        (this is required because the buffer can be overwritten by snmp-set)
- *        if ocstrlen is NULL buffer needs space for terminating 0 byte.
- *        otherwise complete buffer is used for string.
- *        if bufsize is set to 0, the value is regarded as read-only.
- */
-void
-snmp_mib2_set_syslocation(u8_t *ocstr, u16_t *ocstrlen, u16_t bufsize)
-{
-  if (ocstr != NULL) {
-    syslocation         = ocstr;
-    syslocation_wr      = ocstr;
-    syslocation_len     = ocstrlen;
-    syslocation_wr_len  = ocstrlen;
-    syslocation_bufsize = bufsize;
-  }
-  printf("%s\n", syslocation);
-}
-
-/**
- * @ingroup snmp_mib2
- * see \ref snmp_mib2_set_syslocation but set pointer to readonly memory
- */
-void
-snmp_mib2_set_syslocation_readonly(const u8_t *ocstr, const u16_t *ocstrlen)
-{
-  if (ocstr != NULL) {
-    syslocation         = ocstr;
-    syslocation_len     = ocstrlen;
-    syslocation_wr      = NULL;
-    syslocation_wr_len  = NULL;
-    syslocation_bufsize = 0;
-  }
-}
-
-
-static s16_t
-system_get_value(const struct snmp_scalar_array_node_def *node, void *value)
-{
-  const u8_t  *var = NULL;
-  const s16_t *var_len;
-  u16_t result;
-
-  switch (node->oid) {
-    case 1: /* sysDescr */
-      var     = sysdescr;
-      var_len = (const s16_t *)sysdescr_len;
-      break;
-    case 2: { /* sysObjectID */
-      const struct snmp_obj_id *dev_enterprise_oid = snmp_get_device_enterprise_oid();
-      MEMCPY(value, dev_enterprise_oid->id, dev_enterprise_oid->len * sizeof(u32_t));
-      return dev_enterprise_oid->len * sizeof(u32_t);
-    }
-    case 3: /* sysUpTime */
-      MIB2_COPY_SYSUPTIME_TO((u32_t *)value);
-      return sizeof(u32_t);
-    case 4: /* sysContact */
-      var     = syscontact;
-      var_len = (const s16_t *)syscontact_len;
-      break;
-    case 5: /* sysName */
-      var     = sysname;
-      var_len = (const s16_t *)sysname_len;
-      break;
-    case 6: /* sysLocation */
-      var     = syslocation;
-      var_len = (const s16_t *)syslocation_len;
-      break;
-    case 7: /* sysServices */
-      *(s32_t *)value = SNMP_SYSSERVICES;
-      return sizeof(s32_t);
-    default:
-      LWIP_DEBUGF(SNMP_MIB_DEBUG, ("system_get_value(): unknown id: %"S32_F"\n", node->oid));
-      return 0;
-  }
-
-  /* handle string values (OID 1,4,5 and 6) */
-  LWIP_ASSERT("", (value != NULL));
-  if (var_len == NULL) {
-    result = (s16_t)strlen((const char *)var);
-  } else {
-    result = *var_len;
-  }
-  MEMCPY(value, var, result);
-  return result;
-}
-
-static snmp_err_t
-system_set_test(const struct snmp_scalar_array_node_def *node, u16_t len, void *value)
-{
-  snmp_err_t ret = SNMP_ERR_WRONGVALUE;
-  const u16_t *var_bufsize  = NULL;
-  const u16_t *var_wr_len;
-
-  LWIP_UNUSED_ARG(value);
-
-  switch (node->oid) {
-    case 4: /* sysContact */
-      var_bufsize  = &syscontact_bufsize;
-      var_wr_len   = syscontact_wr_len;
-      break;
-    case 5: /* sysName */
-      var_bufsize  = &sysname_bufsize;
-      var_wr_len   = sysname_wr_len;
-      break;
-    case 6: /* sysLocation */
-      var_bufsize  = &syslocation_bufsize;
-      var_wr_len   = syslocation_wr_len;
-      break;
-    default:
-      LWIP_DEBUGF(SNMP_MIB_DEBUG, ("system_set_test(): unknown id: %"S32_F"\n", node->oid));
-      return ret;
-  }
-
-  /* check if value is writable at all */
-  if (*var_bufsize > 0) {
-    if (var_wr_len == NULL) {
-      /* we have to take the terminating 0 into account */
-      if (len < *var_bufsize) {
-        ret = SNMP_ERR_NOERROR;
-      }
-    } else {
-      if (len <= *var_bufsize) {
-        ret = SNMP_ERR_NOERROR;
-      }
-    }
-  } else {
-    ret = SNMP_ERR_NOTWRITABLE;
-  }
-
-  return ret;
-}
-
-static snmp_err_t
-system_set_value(const struct snmp_scalar_array_node_def *node, u16_t len, void *value)
-{
-  u8_t  *var_wr = NULL;
-  u16_t *var_wr_len;
-
-  switch (node->oid) {
-    case 4: /* sysContact */
-      var_wr     = syscontact_wr;
-      var_wr_len = syscontact_wr_len;
-      break;
-    case 5: /* sysName */
-      var_wr     = sysname_wr;
-      var_wr_len = sysname_wr_len;
-      break;
-    case 6: /* sysLocation */
-      var_wr     = syslocation_wr;
-      var_wr_len = syslocation_wr_len;
-      break;
-    default:
-      LWIP_DEBUGF(SNMP_MIB_DEBUG, ("system_set_value(): unknown id: %"S32_F"\n", node->oid));
-      return SNMP_ERR_GENERROR;
-  }
-
-  /* no need to check size of target buffer, this was already done in set_test method */
-  LWIP_ASSERT("", var_wr != NULL);
-  MEMCPY(var_wr, value, len);
-
-  if (var_wr_len == NULL) {
-    /* add terminating 0 */
-    var_wr[len] = 0;
-  } else {
-    *var_wr_len = len;
-  }
-
-  return SNMP_ERR_NOERROR;
-}
-
-static const struct snmp_scalar_array_node_def system_nodes[] = {
-  {1, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY},  /* sysDescr */
-  {2, SNMP_ASN1_TYPE_OBJECT_ID,    SNMP_NODE_INSTANCE_READ_ONLY},  /* sysObjectID */
-  {3, SNMP_ASN1_TYPE_TIMETICKS,    SNMP_NODE_INSTANCE_READ_ONLY},  /* sysUpTime */
-  {4, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_WRITE}, /* sysContact */
-  {5, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_WRITE}, /* sysName */
-  {6, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_WRITE}, /* sysLocation */
-  {7, SNMP_ASN1_TYPE_INTEGER,      SNMP_NODE_INSTANCE_READ_ONLY}   /* sysServices */
-};
-
-const struct snmp_scalar_array_node snmp_mib2_system_node = SNMP_SCALAR_CREATE_ARRAY_NODE(1, system_nodes, system_get_value, system_set_test, system_set_value);
-
-#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 */
+/**
+ * @file
+ * Management Information Base II (RFC1213) SYSTEM objects and functions.
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Dirk Ziegelmeier <dziegel@gmx.de>
+ *         Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#include "lwip/snmp.h"
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "lwip/apps/snmp_mib2.h"
+#include "lwip/apps/snmp_table.h"
+#include "lwip/apps/snmp_scalar.h"
+#include "lwip/sys.h"
+
+#include <string.h>
+
+#if LWIP_SNMP && SNMP_LWIP_MIB2
+
+#if SNMP_USE_NETCONN
+#define SYNC_NODE_NAME(node_name) node_name ## _synced
+#define CREATE_LWIP_SYNC_NODE(oid, node_name) \
+   static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks);
+#else
+#define SYNC_NODE_NAME(node_name) node_name
+#define CREATE_LWIP_SYNC_NODE(oid, node_name)
+#endif
+
+/* --- system .1.3.6.1.2.1.1 ----------------------------------------------------- */
+
+/** mib-2.system.sysDescr */
+static const u8_t   sysdescr_default[] = SNMP_LWIP_MIB2_SYSDESC;
+static const u8_t*  sysdescr           = sysdescr_default;
+static const u16_t* sysdescr_len       = NULL; /* use strlen for determining len */
+
+/** mib-2.system.sysContact */
+static const u8_t   syscontact_default[]     = SNMP_LWIP_MIB2_SYSCONTACT;
+static const u8_t*  syscontact               = syscontact_default;
+static const u16_t* syscontact_len           = NULL; /* use strlen for determining len */
+static u8_t*        syscontact_wr            = NULL; /* if writable, points to the same buffer as syscontact (required for correct constness) */
+static u16_t*       syscontact_wr_len        = NULL; /* if writable, points to the same buffer as syscontact_len (required for correct constness) */
+static u16_t        syscontact_bufsize       = 0;    /* 0=not writable */
+
+/** mib-2.system.sysName */
+static const u8_t   sysname_default[]        = SNMP_LWIP_MIB2_SYSNAME;
+static const u8_t*  sysname                  = sysname_default;
+static const u16_t* sysname_len              = NULL; /* use strlen for determining len */
+static u8_t*        sysname_wr               = NULL; /* if writable, points to the same buffer as sysname (required for correct constness) */
+static u16_t*       sysname_wr_len           = NULL; /* if writable, points to the same buffer as sysname_len (required for correct constness) */
+static u16_t        sysname_bufsize          = 0;    /* 0=not writable */
+
+/** mib-2.system.sysLocation */
+static const u8_t   syslocation_default[]    = SNMP_LWIP_MIB2_SYSLOCATION;
+static const u8_t*  syslocation              = syslocation_default;
+static const u16_t* syslocation_len           = NULL; /* use strlen for determining len */
+static u8_t*        syslocation_wr            = NULL; /* if writable, points to the same buffer as syslocation (required for correct constness) */
+static u16_t*       syslocation_wr_len        = NULL; /* if writable, points to the same buffer as syslocation_len (required for correct constness) */
+static u16_t        syslocation_bufsize       = 0;    /* 0=not writable */
+
+/**
+ * @ingroup snmp_mib2
+ * Initializes sysDescr pointers.
+ *
+ * @param str if non-NULL then copy str pointer
+ * @param len points to string length, excluding zero terminator
+ */
+void
+snmp_mib2_set_sysdescr(const u8_t *str, const u16_t *len)
+{
+  if (str != NULL) {
+    sysdescr     = str;
+    sysdescr_len = len;
+  }
+}
+
+/**
+ * @ingroup snmp_mib2
+ * Initializes sysContact pointers
+ *
+ * @param ocstr if non-NULL then copy str pointer
+ * @param ocstrlen points to string length, excluding zero terminator.
+ *        if set to NULL it is assumed that ocstr is NULL-terminated.
+ * @param bufsize size of the buffer in bytes.
+ *        (this is required because the buffer can be overwritten by snmp-set)
+ *        if ocstrlen is NULL buffer needs space for terminating 0 byte.
+ *        otherwise complete buffer is used for string.
+ *        if bufsize is set to 0, the value is regarded as read-only.
+ */
+void
+snmp_mib2_set_syscontact(u8_t *ocstr, u16_t *ocstrlen, u16_t bufsize)
+{
+  if (ocstr != NULL) {
+    syscontact         = ocstr;
+    syscontact_wr      = ocstr;
+    syscontact_len     = ocstrlen;
+    syscontact_wr_len  = ocstrlen;
+    syscontact_bufsize = bufsize;
+  }
+}
+
+/**
+ * @ingroup snmp_mib2
+ * see \ref snmp_mib2_set_syscontact but set pointer to readonly memory
+ */
+void
+snmp_mib2_set_syscontact_readonly(const u8_t *ocstr, const u16_t *ocstrlen)
+{
+  if (ocstr != NULL) {
+    syscontact         = ocstr;
+    syscontact_len     = ocstrlen;
+    syscontact_wr      = NULL;
+    syscontact_wr_len  = NULL;
+    syscontact_bufsize = 0;
+  }
+}
+
+
+/**
+ * @ingroup snmp_mib2
+ * Initializes sysName pointers
+ *
+ * @param ocstr if non-NULL then copy str pointer
+ * @param ocstrlen points to string length, excluding zero terminator.
+ *        if set to NULL it is assumed that ocstr is NULL-terminated.
+ * @param bufsize size of the buffer in bytes.
+ *        (this is required because the buffer can be overwritten by snmp-set)
+ *        if ocstrlen is NULL buffer needs space for terminating 0 byte.
+ *        otherwise complete buffer is used for string.
+ *        if bufsize is set to 0, the value is regarded as read-only.
+ */
+void
+snmp_mib2_set_sysname(u8_t *ocstr, u16_t *ocstrlen, u16_t bufsize)
+{
+  if (ocstr != NULL) {
+    sysname         = ocstr;
+    sysname_wr      = ocstr;
+    sysname_len     = ocstrlen;
+    sysname_wr_len  = ocstrlen;
+    sysname_bufsize = bufsize;
+  }
+}
+
+/**
+ * @ingroup snmp_mib2
+ * see \ref snmp_mib2_set_sysname but set pointer to readonly memory
+ */
+void
+snmp_mib2_set_sysname_readonly(const u8_t *ocstr, const u16_t *ocstrlen)
+{
+  if (ocstr != NULL) {
+    sysname         = ocstr;
+    sysname_len     = ocstrlen;
+    sysname_wr      = NULL;
+    sysname_wr_len  = NULL;
+    sysname_bufsize = 0;
+  }
+}
+
+/**
+ * @ingroup snmp_mib2
+ * Initializes sysLocation pointers
+ *
+ * @param ocstr if non-NULL then copy str pointer
+ * @param ocstrlen points to string length, excluding zero terminator.
+ *        if set to NULL it is assumed that ocstr is NULL-terminated.
+ * @param bufsize size of the buffer in bytes.
+ *        (this is required because the buffer can be overwritten by snmp-set)
+ *        if ocstrlen is NULL buffer needs space for terminating 0 byte.
+ *        otherwise complete buffer is used for string.
+ *        if bufsize is set to 0, the value is regarded as read-only.
+ */
+void
+snmp_mib2_set_syslocation(u8_t *ocstr, u16_t *ocstrlen, u16_t bufsize)
+{
+  if (ocstr != NULL) {
+    syslocation         = ocstr;
+    syslocation_wr      = ocstr;
+    syslocation_len     = ocstrlen;
+    syslocation_wr_len  = ocstrlen;
+    syslocation_bufsize = bufsize;
+  }
+}
+
+/**
+ * @ingroup snmp_mib2
+ * see \ref snmp_mib2_set_syslocation but set pointer to readonly memory
+ */
+void
+snmp_mib2_set_syslocation_readonly(const u8_t *ocstr, const u16_t *ocstrlen)
+{
+  if (ocstr != NULL) {
+    syslocation         = ocstr;
+    syslocation_len     = ocstrlen;
+    syslocation_wr      = NULL;
+    syslocation_wr_len  = NULL;
+    syslocation_bufsize = 0;
+  }
+}
+
+
+static s16_t
+system_get_value(const struct snmp_scalar_array_node_def *node, void *value)
+{
+  const u8_t*  var = NULL;
+  const s16_t* var_len;
+  u16_t result;
+
+  switch (node->oid) {
+  case 1: /* sysDescr */
+    var     = sysdescr;
+    var_len = (const s16_t*)sysdescr_len;
+    break;
+  case 2: /* sysObjectID */
+    {
+      const struct snmp_obj_id* dev_enterprise_oid = snmp_get_device_enterprise_oid();
+      MEMCPY(value, dev_enterprise_oid->id, dev_enterprise_oid->len * sizeof(u32_t));
+      return dev_enterprise_oid->len * sizeof(u32_t);
+    }
+  case 3: /* sysUpTime */
+    MIB2_COPY_SYSUPTIME_TO((u32_t*)value);
+    return sizeof(u32_t);
+  case 4: /* sysContact */
+    var     = syscontact;
+    var_len = (const s16_t*)syscontact_len;
+    break;
+  case 5: /* sysName */
+    var     = sysname;
+    var_len = (const s16_t*)sysname_len;
+    break;
+  case 6: /* sysLocation */
+    var     = syslocation;
+    var_len = (const s16_t*)syslocation_len;
+    break;
+  case 7: /* sysServices */
+    *(s32_t*)value = SNMP_SYSSERVICES;
+    return sizeof(s32_t);
+  default:
+    LWIP_DEBUGF(SNMP_MIB_DEBUG,("system_get_value(): unknown id: %"S32_F"\n", node->oid));
+    return 0;
+  }
+
+  /* handle string values (OID 1,4,5 and 6) */
+  LWIP_ASSERT("value", (value != NULL));
+  if (var_len == NULL) {
+    result = (s16_t)strlen((const char*)var);
+  } else {
+    result = *var_len;
+  }
+  MEMCPY(value, var, result);
+  return result;
+}
+
+static snmp_err_t
+system_set_test(const struct snmp_scalar_array_node_def *node, u16_t len, void *value)
+{
+  snmp_err_t ret = SNMP_ERR_WRONGVALUE;
+  const u16_t* var_bufsize  = NULL;
+  const u16_t* var_wr_len;
+
+  LWIP_UNUSED_ARG(value);
+
+  switch (node->oid) {
+  case 4: /* sysContact */
+    var_bufsize  = &syscontact_bufsize;
+    var_wr_len   = syscontact_wr_len;
+    break;
+  case 5: /* sysName */
+    var_bufsize  = &sysname_bufsize;
+    var_wr_len   = sysname_wr_len;
+    break;
+  case 6: /* sysLocation */
+    var_bufsize  = &syslocation_bufsize;
+    var_wr_len   = syslocation_wr_len;
+    break;
+  default:
+    LWIP_DEBUGF(SNMP_MIB_DEBUG,("system_set_test(): unknown id: %"S32_F"\n", node->oid));
+    return ret;
+  }
+
+  /* check if value is writable at all */
+  if (*var_bufsize > 0) {
+    if (var_wr_len == NULL) {
+      /* we have to take the terminating 0 into account */
+      if (len < *var_bufsize) {
+        ret = SNMP_ERR_NOERROR;
+      }
+    } else {
+      if (len <= *var_bufsize) {
+        ret = SNMP_ERR_NOERROR;
+      }
+    }
+  } else {
+    ret = SNMP_ERR_NOTWRITABLE;
+  }
+
+  return ret;
+}
+
+static snmp_err_t
+system_set_value(const struct snmp_scalar_array_node_def *node, u16_t len, void *value)
+{
+  u8_t*  var_wr = NULL;
+  u16_t* var_wr_len;
+
+  switch (node->oid) {
+  case 4: /* sysContact */
+    var_wr     = syscontact_wr;
+    var_wr_len = syscontact_wr_len;
+    break;
+  case 5: /* sysName */
+    var_wr     = sysname_wr;
+    var_wr_len = sysname_wr_len;
+    break;
+  case 6: /* sysLocation */
+    var_wr     = syslocation_wr;
+    var_wr_len = syslocation_wr_len;
+    break;
+  default:
+    LWIP_DEBUGF(SNMP_MIB_DEBUG,("system_set_value(): unknown id: %"S32_F"\n", node->oid));
+    return SNMP_ERR_GENERROR;
+  }
+
+  /* no need to check size of target buffer, this was already done in set_test method */
+  LWIP_ASSERT("val_wr", var_wr != NULL);
+  MEMCPY(var_wr, value, len);
+  
+  if (var_wr_len == NULL) {
+    /* add terminating 0 */
+    var_wr[len] = 0;
+  } else {
+    *var_wr_len = len;
+  }
+
+  return SNMP_ERR_NOERROR;
+}
+
+static const struct snmp_scalar_array_node_def system_nodes[] = {
+  {1, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY},  /* sysDescr */
+  {2, SNMP_ASN1_TYPE_OBJECT_ID,    SNMP_NODE_INSTANCE_READ_ONLY},  /* sysObjectID */
+  {3, SNMP_ASN1_TYPE_TIMETICKS,    SNMP_NODE_INSTANCE_READ_ONLY},  /* sysUpTime */
+  {4, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_WRITE}, /* sysContact */
+  {5, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_WRITE}, /* sysName */
+  {6, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_WRITE}, /* sysLocation */
+  {7, SNMP_ASN1_TYPE_INTEGER,      SNMP_NODE_INSTANCE_READ_ONLY}   /* sysServices */
+};
+
+const struct snmp_scalar_array_node snmp_mib2_system_node = SNMP_SCALAR_CREATE_ARRAY_NODE(1, system_nodes, system_get_value, system_set_test, system_set_value);
+
+#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 */

+ 594 - 607
libs/thirdparty/lwip_2.1.2/src/apps/snmp/snmp_mib2_tcp.c → libs/thirdparty/LwIP/src/apps/snmp/snmp_mib2_tcp.c

@@ -1,607 +1,594 @@
-/**
- * @file
- * Management Information Base II (RFC1213) TCP objects and functions.
- */
-
-/*
- * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * Author: Dirk Ziegelmeier <dziegel@gmx.de>
- *         Christiaan Simons <christiaan.simons@axon.tv>
- */
-
-#include "lwip/snmp.h"
-#include "lwip/apps/snmp.h"
-#include "lwip/apps/snmp_core.h"
-#include "lwip/apps/snmp_mib2.h"
-#include "lwip/apps/snmp_table.h"
-#include "lwip/apps/snmp_scalar.h"
-#include "lwip/tcp.h"
-#include "lwip/priv/tcp_priv.h"
-#include "lwip/stats.h"
-
-#include <string.h>
-
-#if LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_TCP
-
-#if SNMP_USE_NETCONN
-#define SYNC_NODE_NAME(node_name) node_name ## _synced
-#define CREATE_LWIP_SYNC_NODE(oid, node_name) \
-   static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks);
-#else
-#define SYNC_NODE_NAME(node_name) node_name
-#define CREATE_LWIP_SYNC_NODE(oid, node_name)
-#endif
-
-/* --- tcp .1.3.6.1.2.1.6 ----------------------------------------------------- */
-
-static s16_t
-tcp_get_value(struct snmp_node_instance *instance, void *value)
-{
-  u32_t *uint_ptr = (u32_t *)value;
-  s32_t *sint_ptr = (s32_t *)value;
-
-  switch (instance->node->oid) {
-    case 1: /* tcpRtoAlgorithm, vanj(4) */
-      *sint_ptr = 4;
-      return sizeof(*sint_ptr);
-    case 2: /* tcpRtoMin */
-      /* @todo not the actual value, a guess,
-          needs to be calculated */
-      *sint_ptr = 1000;
-      return sizeof(*sint_ptr);
-    case 3: /* tcpRtoMax */
-      /* @todo not the actual value, a guess,
-          needs to be calculated */
-      *sint_ptr = 60000;
-      return sizeof(*sint_ptr);
-    case 4: /* tcpMaxConn */
-      *sint_ptr = MEMP_NUM_TCP_PCB;
-      return sizeof(*sint_ptr);
-    case 5: /* tcpActiveOpens */
-      *uint_ptr = STATS_GET(mib2.tcpactiveopens);
-      return sizeof(*uint_ptr);
-    case 6: /* tcpPassiveOpens */
-      *uint_ptr = STATS_GET(mib2.tcppassiveopens);
-      return sizeof(*uint_ptr);
-    case 7: /* tcpAttemptFails */
-      *uint_ptr = STATS_GET(mib2.tcpattemptfails);
-      return sizeof(*uint_ptr);
-    case 8: /* tcpEstabResets */
-      *uint_ptr = STATS_GET(mib2.tcpestabresets);
-      return sizeof(*uint_ptr);
-    case 9: { /* tcpCurrEstab */
-      u16_t tcpcurrestab = 0;
-      struct tcp_pcb *pcb = tcp_active_pcbs;
-      while (pcb != NULL) {
-        if ((pcb->state == ESTABLISHED) ||
-            (pcb->state == CLOSE_WAIT)) {
-          tcpcurrestab++;
-        }
-        pcb = pcb->next;
-      }
-      *uint_ptr = tcpcurrestab;
-    }
-    return sizeof(*uint_ptr);
-    case 10: /* tcpInSegs */
-      *uint_ptr = STATS_GET(mib2.tcpinsegs);
-      return sizeof(*uint_ptr);
-    case 11: /* tcpOutSegs */
-      *uint_ptr = STATS_GET(mib2.tcpoutsegs);
-      return sizeof(*uint_ptr);
-    case 12: /* tcpRetransSegs */
-      *uint_ptr = STATS_GET(mib2.tcpretranssegs);
-      return sizeof(*uint_ptr);
-    case 14: /* tcpInErrs */
-      *uint_ptr = STATS_GET(mib2.tcpinerrs);
-      return sizeof(*uint_ptr);
-    case 15: /* tcpOutRsts */
-      *uint_ptr = STATS_GET(mib2.tcpoutrsts);
-      return sizeof(*uint_ptr);
-#if LWIP_HAVE_INT64
-    case 17: { /* tcpHCInSegs */
-      /* use the 32 bit counter for now... */
-      u64_t val64 = STATS_GET(mib2.tcpinsegs);
-      *((u64_t *)value) = val64;
-    }
-    return sizeof(u64_t);
-    case 18: { /* tcpHCOutSegs */
-      /* use the 32 bit counter for now... */
-      u64_t val64 = STATS_GET(mib2.tcpoutsegs);
-      *((u64_t *)value) = val64;
-    }
-    return sizeof(u64_t);
-#endif
-    default:
-      LWIP_DEBUGF(SNMP_MIB_DEBUG, ("tcp_get_value(): unknown id: %"S32_F"\n", instance->node->oid));
-      break;
-  }
-
-  return 0;
-}
-
-/* --- tcpConnTable --- */
-
-#if LWIP_IPV4
-
-/* list of allowed value ranges for incoming OID */
-static const struct snmp_oid_range tcp_ConnTable_oid_ranges[] = {
-  { 0, 0xff   }, /* IP A */
-  { 0, 0xff   }, /* IP B */
-  { 0, 0xff   }, /* IP C */
-  { 0, 0xff   }, /* IP D */
-  { 0, 0xffff }, /* Port */
-  { 0, 0xff   }, /* IP A */
-  { 0, 0xff   }, /* IP B */
-  { 0, 0xff   }, /* IP C */
-  { 0, 0xff   }, /* IP D */
-  { 0, 0xffff }  /* Port */
-};
-
-static snmp_err_t
-tcp_ConnTable_get_cell_value_core(struct tcp_pcb *pcb, const u32_t *column, union snmp_variant_value *value, u32_t *value_len)
-{
-  LWIP_UNUSED_ARG(value_len);
-
-  /* value */
-  switch (*column) {
-    case 1: /* tcpConnState */
-      value->u32 = pcb->state + 1;
-      break;
-    case 2: /* tcpConnLocalAddress */
-      value->u32 = ip_2_ip4(&pcb->local_ip)->addr;
-      break;
-    case 3: /* tcpConnLocalPort */
-      value->u32 = pcb->local_port;
-      break;
-    case 4: /* tcpConnRemAddress */
-      if (pcb->state == LISTEN) {
-        value->u32 = IP4_ADDR_ANY4->addr;
-      } else {
-        value->u32 = ip_2_ip4(&pcb->remote_ip)->addr;
-      }
-      break;
-    case 5: /* tcpConnRemPort */
-      if (pcb->state == LISTEN) {
-        value->u32 = 0;
-      } else {
-        value->u32 = pcb->remote_port;
-      }
-      break;
-    default:
-      LWIP_ASSERT("invalid id", 0);
-      return SNMP_ERR_NOSUCHINSTANCE;
-  }
-
-  return SNMP_ERR_NOERROR;
-}
-
-static snmp_err_t
-tcp_ConnTable_get_cell_value(const u32_t *column, const u32_t *row_oid, u8_t row_oid_len, union snmp_variant_value *value, u32_t *value_len)
-{
-  u8_t i;
-  ip4_addr_t local_ip;
-  ip4_addr_t remote_ip;
-  u16_t local_port;
-  u16_t remote_port;
-  struct tcp_pcb *pcb;
-
-  /* check if incoming OID length and if values are in plausible range */
-  if (!snmp_oid_in_range(row_oid, row_oid_len, tcp_ConnTable_oid_ranges, LWIP_ARRAYSIZE(tcp_ConnTable_oid_ranges))) {
-    return SNMP_ERR_NOSUCHINSTANCE;
-  }
-
-  /* get IPs and ports from incoming OID */
-  snmp_oid_to_ip4(&row_oid[0], &local_ip); /* we know it succeeds because of oid_in_range check above */
-  local_port = (u16_t)row_oid[4];
-  snmp_oid_to_ip4(&row_oid[5], &remote_ip); /* we know it succeeds because of oid_in_range check above */
-  remote_port = (u16_t)row_oid[9];
-
-  /* find tcp_pcb with requested ips and ports */
-  for (i = 0; i < LWIP_ARRAYSIZE(tcp_pcb_lists); i++) {
-    pcb = *tcp_pcb_lists[i];
-
-    while (pcb != NULL) {
-      /* do local IP and local port match? */
-      if (IP_IS_V4_VAL(pcb->local_ip) &&
-          ip4_addr_cmp(&local_ip, ip_2_ip4(&pcb->local_ip)) && (local_port == pcb->local_port)) {
-
-        /* PCBs in state LISTEN are not connected and have no remote_ip or remote_port */
-        if (pcb->state == LISTEN) {
-          if (ip4_addr_cmp(&remote_ip, IP4_ADDR_ANY4) && (remote_port == 0)) {
-            /* fill in object properties */
-            return tcp_ConnTable_get_cell_value_core(pcb, column, value, value_len);
-          }
-        } else {
-          if (IP_IS_V4_VAL(pcb->remote_ip) &&
-              ip4_addr_cmp(&remote_ip, ip_2_ip4(&pcb->remote_ip)) && (remote_port == pcb->remote_port)) {
-            /* fill in object properties */
-            return tcp_ConnTable_get_cell_value_core(pcb, column, value, value_len);
-          }
-        }
-      }
-
-      pcb = pcb->next;
-    }
-  }
-
-  /* not found */
-  return SNMP_ERR_NOSUCHINSTANCE;
-}
-
-static snmp_err_t
-tcp_ConnTable_get_next_cell_instance_and_value(const u32_t *column, struct snmp_obj_id *row_oid, union snmp_variant_value *value, u32_t *value_len)
-{
-  u8_t i;
-  struct tcp_pcb *pcb;
-  struct snmp_next_oid_state state;
-  u32_t result_temp[LWIP_ARRAYSIZE(tcp_ConnTable_oid_ranges)];
-
-  /* init struct to search next oid */
-  snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(tcp_ConnTable_oid_ranges));
-
-  /* iterate over all possible OIDs to find the next one */
-  for (i = 0; i < LWIP_ARRAYSIZE(tcp_pcb_lists); i++) {
-    pcb = *tcp_pcb_lists[i];
-    while (pcb != NULL) {
-      u32_t test_oid[LWIP_ARRAYSIZE(tcp_ConnTable_oid_ranges)];
-
-      if (IP_IS_V4_VAL(pcb->local_ip)) {
-        snmp_ip4_to_oid(ip_2_ip4(&pcb->local_ip), &test_oid[0]);
-        test_oid[4] = pcb->local_port;
-
-        /* PCBs in state LISTEN are not connected and have no remote_ip or remote_port */
-        if (pcb->state == LISTEN) {
-          snmp_ip4_to_oid(IP4_ADDR_ANY4, &test_oid[5]);
-          test_oid[9] = 0;
-        } else {
-          if (IP_IS_V6_VAL(pcb->remote_ip)) { /* should never happen */
-            continue;
-          }
-          snmp_ip4_to_oid(ip_2_ip4(&pcb->remote_ip), &test_oid[5]);
-          test_oid[9] = pcb->remote_port;
-        }
-
-        /* check generated OID: is it a candidate for the next one? */
-        snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(tcp_ConnTable_oid_ranges), pcb);
-      }
-
-      pcb = pcb->next;
-    }
-  }
-
-  /* did we find a next one? */
-  if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
-    snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
-    /* fill in object properties */
-    return tcp_ConnTable_get_cell_value_core((struct tcp_pcb *)state.reference, column, value, value_len);
-  }
-
-  /* not found */
-  return SNMP_ERR_NOSUCHINSTANCE;
-}
-
-#endif /* LWIP_IPV4 */
-
-/* --- tcpConnectionTable --- */
-
-static snmp_err_t
-tcp_ConnectionTable_get_cell_value_core(const u32_t *column, struct tcp_pcb *pcb, union snmp_variant_value *value)
-{
-  /* all items except tcpConnectionState and tcpConnectionProcess are declared as not-accessible */
-  switch (*column) {
-    case 7: /* tcpConnectionState */
-      value->u32 = pcb->state + 1;
-      break;
-    case 8: /* tcpConnectionProcess */
-      value->u32 = 0; /* not supported */
-      break;
-    default:
-      return SNMP_ERR_NOSUCHINSTANCE;
-  }
-
-  return SNMP_ERR_NOERROR;
-}
-
-static snmp_err_t
-tcp_ConnectionTable_get_cell_value(const u32_t *column, const u32_t *row_oid, u8_t row_oid_len, union snmp_variant_value *value, u32_t *value_len)
-{
-  ip_addr_t local_ip, remote_ip;
-  u16_t local_port, remote_port;
-  struct tcp_pcb *pcb;
-  u8_t idx = 0;
-  u8_t i;
-  struct tcp_pcb **const tcp_pcb_nonlisten_lists[] = {&tcp_bound_pcbs, &tcp_active_pcbs, &tcp_tw_pcbs};
-
-  LWIP_UNUSED_ARG(value_len);
-
-  /* tcpConnectionLocalAddressType + tcpConnectionLocalAddress + tcpConnectionLocalPort */
-  idx += snmp_oid_to_ip_port(&row_oid[idx], row_oid_len - idx, &local_ip, &local_port);
-  if (idx == 0) {
-    return SNMP_ERR_NOSUCHINSTANCE;
-  }
-
-  /* tcpConnectionRemAddressType + tcpConnectionRemAddress + tcpConnectionRemPort */
-  idx += snmp_oid_to_ip_port(&row_oid[idx], row_oid_len - idx, &remote_ip, &remote_port);
-  if (idx == 0) {
-    return SNMP_ERR_NOSUCHINSTANCE;
-  }
-
-  /* find tcp_pcb with requested ip and port*/
-  for (i = 0; i < LWIP_ARRAYSIZE(tcp_pcb_nonlisten_lists); i++) {
-    pcb = *tcp_pcb_nonlisten_lists[i];
-
-    while (pcb != NULL) {
-      if (ip_addr_cmp(&local_ip, &pcb->local_ip) &&
-          (local_port == pcb->local_port) &&
-          ip_addr_cmp(&remote_ip, &pcb->remote_ip) &&
-          (remote_port == pcb->remote_port)) {
-        /* fill in object properties */
-        return tcp_ConnectionTable_get_cell_value_core(column, pcb, value);
-      }
-      pcb = pcb->next;
-    }
-  }
-
-  /* not found */
-  return SNMP_ERR_NOSUCHINSTANCE;
-}
-
-static snmp_err_t
-tcp_ConnectionTable_get_next_cell_instance_and_value(const u32_t *column, struct snmp_obj_id *row_oid, union snmp_variant_value *value, u32_t *value_len)
-{
-  struct tcp_pcb *pcb;
-  struct snmp_next_oid_state state;
-  /* 1x tcpConnectionLocalAddressType + 1x OID len + 16x tcpConnectionLocalAddress  + 1x tcpConnectionLocalPort
-   * 1x tcpConnectionRemAddressType   + 1x OID len + 16x tcpConnectionRemAddress    + 1x tcpConnectionRemPort */
-  u32_t  result_temp[38];
-  u8_t i;
-  struct tcp_pcb **const tcp_pcb_nonlisten_lists[] = {&tcp_bound_pcbs, &tcp_active_pcbs, &tcp_tw_pcbs};
-
-  LWIP_UNUSED_ARG(value_len);
-
-  /* init struct to search next oid */
-  snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(result_temp));
-
-  /* iterate over all possible OIDs to find the next one */
-  for (i = 0; i < LWIP_ARRAYSIZE(tcp_pcb_nonlisten_lists); i++) {
-    pcb = *tcp_pcb_nonlisten_lists[i];
-
-    while (pcb != NULL) {
-      u8_t idx = 0;
-      u32_t test_oid[LWIP_ARRAYSIZE(result_temp)];
-
-      /* tcpConnectionLocalAddressType + tcpConnectionLocalAddress + tcpConnectionLocalPort */
-      idx += snmp_ip_port_to_oid(&pcb->local_ip, pcb->local_port, &test_oid[idx]);
-
-      /* tcpConnectionRemAddressType + tcpConnectionRemAddress + tcpConnectionRemPort */
-      idx += snmp_ip_port_to_oid(&pcb->remote_ip, pcb->remote_port, &test_oid[idx]);
-
-      /* check generated OID: is it a candidate for the next one? */
-      snmp_next_oid_check(&state, test_oid, idx, pcb);
-
-      pcb = pcb->next;
-    }
-  }
-
-  /* did we find a next one? */
-  if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
-    snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
-    /* fill in object properties */
-    return tcp_ConnectionTable_get_cell_value_core(column, (struct tcp_pcb *)state.reference, value);
-  } else {
-    /* not found */
-    return SNMP_ERR_NOSUCHINSTANCE;
-  }
-}
-
-/* --- tcpListenerTable --- */
-
-static snmp_err_t
-tcp_ListenerTable_get_cell_value_core(const u32_t *column, union snmp_variant_value *value)
-{
-  /* all items except tcpListenerProcess are declared as not-accessible */
-  switch (*column) {
-    case 4: /* tcpListenerProcess */
-      value->u32 = 0; /* not supported */
-      break;
-    default:
-      return SNMP_ERR_NOSUCHINSTANCE;
-  }
-
-  return SNMP_ERR_NOERROR;
-}
-
-static snmp_err_t
-tcp_ListenerTable_get_cell_value(const u32_t *column, const u32_t *row_oid, u8_t row_oid_len, union snmp_variant_value *value, u32_t *value_len)
-{
-  ip_addr_t local_ip;
-  u16_t local_port;
-  struct tcp_pcb_listen *pcb;
-  u8_t idx = 0;
-
-  LWIP_UNUSED_ARG(value_len);
-
-  /* tcpListenerLocalAddressType + tcpListenerLocalAddress + tcpListenerLocalPort */
-  idx += snmp_oid_to_ip_port(&row_oid[idx], row_oid_len - idx, &local_ip, &local_port);
-  if (idx == 0) {
-    return SNMP_ERR_NOSUCHINSTANCE;
-  }
-
-  /* find tcp_pcb with requested ip and port*/
-  pcb = tcp_listen_pcbs.listen_pcbs;
-  while (pcb != NULL) {
-    if (ip_addr_cmp(&local_ip, &pcb->local_ip) &&
-        (local_port == pcb->local_port)) {
-      /* fill in object properties */
-      return tcp_ListenerTable_get_cell_value_core(column, value);
-    }
-    pcb = pcb->next;
-  }
-
-  /* not found */
-  return SNMP_ERR_NOSUCHINSTANCE;
-}
-
-static snmp_err_t
-tcp_ListenerTable_get_next_cell_instance_and_value(const u32_t *column, struct snmp_obj_id *row_oid, union snmp_variant_value *value, u32_t *value_len)
-{
-  struct tcp_pcb_listen *pcb;
-  struct snmp_next_oid_state state;
-  /* 1x tcpListenerLocalAddressType + 1x OID len + 16x tcpListenerLocalAddress  + 1x tcpListenerLocalPort */
-  u32_t  result_temp[19];
-
-  LWIP_UNUSED_ARG(value_len);
-
-  /* init struct to search next oid */
-  snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(result_temp));
-
-  /* iterate over all possible OIDs to find the next one */
-  pcb = tcp_listen_pcbs.listen_pcbs;
-  while (pcb != NULL) {
-    u8_t idx = 0;
-    u32_t test_oid[LWIP_ARRAYSIZE(result_temp)];
-
-    /* tcpListenerLocalAddressType + tcpListenerLocalAddress + tcpListenerLocalPort */
-    idx += snmp_ip_port_to_oid(&pcb->local_ip, pcb->local_port, &test_oid[idx]);
-
-    /* check generated OID: is it a candidate for the next one? */
-    snmp_next_oid_check(&state, test_oid, idx, NULL);
-
-    pcb = pcb->next;
-  }
-
-  /* did we find a next one? */
-  if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
-    snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
-    /* fill in object properties */
-    return tcp_ListenerTable_get_cell_value_core(column, value);
-  } else {
-    /* not found */
-    return SNMP_ERR_NOSUCHINSTANCE;
-  }
-}
-
-static const struct snmp_scalar_node tcp_RtoAlgorithm  = SNMP_SCALAR_CREATE_NODE_READONLY(1, SNMP_ASN1_TYPE_INTEGER, tcp_get_value);
-static const struct snmp_scalar_node tcp_RtoMin        = SNMP_SCALAR_CREATE_NODE_READONLY(2, SNMP_ASN1_TYPE_INTEGER, tcp_get_value);
-static const struct snmp_scalar_node tcp_RtoMax        = SNMP_SCALAR_CREATE_NODE_READONLY(3, SNMP_ASN1_TYPE_INTEGER, tcp_get_value);
-static const struct snmp_scalar_node tcp_MaxConn       = SNMP_SCALAR_CREATE_NODE_READONLY(4, SNMP_ASN1_TYPE_INTEGER, tcp_get_value);
-static const struct snmp_scalar_node tcp_ActiveOpens   = SNMP_SCALAR_CREATE_NODE_READONLY(5, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
-static const struct snmp_scalar_node tcp_PassiveOpens  = SNMP_SCALAR_CREATE_NODE_READONLY(6, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
-static const struct snmp_scalar_node tcp_AttemptFails  = SNMP_SCALAR_CREATE_NODE_READONLY(7, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
-static const struct snmp_scalar_node tcp_EstabResets   = SNMP_SCALAR_CREATE_NODE_READONLY(8, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
-static const struct snmp_scalar_node tcp_CurrEstab     = SNMP_SCALAR_CREATE_NODE_READONLY(9, SNMP_ASN1_TYPE_GAUGE, tcp_get_value);
-static const struct snmp_scalar_node tcp_InSegs        = SNMP_SCALAR_CREATE_NODE_READONLY(10, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
-static const struct snmp_scalar_node tcp_OutSegs       = SNMP_SCALAR_CREATE_NODE_READONLY(11, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
-static const struct snmp_scalar_node tcp_RetransSegs   = SNMP_SCALAR_CREATE_NODE_READONLY(12, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
-static const struct snmp_scalar_node tcp_InErrs        = SNMP_SCALAR_CREATE_NODE_READONLY(14, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
-static const struct snmp_scalar_node tcp_OutRsts       = SNMP_SCALAR_CREATE_NODE_READONLY(15, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
-#if LWIP_HAVE_INT64
-static const struct snmp_scalar_node tcp_HCInSegs      = SNMP_SCALAR_CREATE_NODE_READONLY(17, SNMP_ASN1_TYPE_COUNTER64, tcp_get_value);
-static const struct snmp_scalar_node tcp_HCOutSegs     = SNMP_SCALAR_CREATE_NODE_READONLY(18, SNMP_ASN1_TYPE_COUNTER64, tcp_get_value);
-#endif
-
-#if LWIP_IPV4
-static const struct snmp_table_simple_col_def tcp_ConnTable_columns[] = {
-  {  1, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* tcpConnState */
-  {  2, SNMP_ASN1_TYPE_IPADDR,  SNMP_VARIANT_VALUE_TYPE_U32 }, /* tcpConnLocalAddress */
-  {  3, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* tcpConnLocalPort */
-  {  4, SNMP_ASN1_TYPE_IPADDR,  SNMP_VARIANT_VALUE_TYPE_U32 }, /* tcpConnRemAddress */
-  {  5, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }  /* tcpConnRemPort */
-};
-
-static const struct snmp_table_simple_node tcp_ConnTable = SNMP_TABLE_CREATE_SIMPLE(13, tcp_ConnTable_columns, tcp_ConnTable_get_cell_value, tcp_ConnTable_get_next_cell_instance_and_value);
-#endif /* LWIP_IPV4 */
-
-static const struct snmp_table_simple_col_def tcp_ConnectionTable_columns[] = {
-  /* all items except tcpConnectionState and tcpConnectionProcess are declared as not-accessible */
-  { 7, SNMP_ASN1_TYPE_INTEGER,    SNMP_VARIANT_VALUE_TYPE_U32 }, /* tcpConnectionState */
-  { 8, SNMP_ASN1_TYPE_UNSIGNED32, SNMP_VARIANT_VALUE_TYPE_U32 }  /* tcpConnectionProcess */
-};
-
-static const struct snmp_table_simple_node tcp_ConnectionTable = SNMP_TABLE_CREATE_SIMPLE(19, tcp_ConnectionTable_columns, tcp_ConnectionTable_get_cell_value, tcp_ConnectionTable_get_next_cell_instance_and_value);
-
-
-static const struct snmp_table_simple_col_def tcp_ListenerTable_columns[] = {
-  /* all items except tcpListenerProcess are declared as not-accessible */
-  { 4, SNMP_ASN1_TYPE_UNSIGNED32, SNMP_VARIANT_VALUE_TYPE_U32 }  /* tcpListenerProcess */
-};
-
-static const struct snmp_table_simple_node tcp_ListenerTable = SNMP_TABLE_CREATE_SIMPLE(20, tcp_ListenerTable_columns, tcp_ListenerTable_get_cell_value, tcp_ListenerTable_get_next_cell_instance_and_value);
-
-/* the following nodes access variables in LWIP stack from SNMP worker thread and must therefore be synced to LWIP (TCPIP) thread */
-CREATE_LWIP_SYNC_NODE( 1, tcp_RtoAlgorithm)
-CREATE_LWIP_SYNC_NODE( 2, tcp_RtoMin)
-CREATE_LWIP_SYNC_NODE( 3, tcp_RtoMax)
-CREATE_LWIP_SYNC_NODE( 4, tcp_MaxConn)
-CREATE_LWIP_SYNC_NODE( 5, tcp_ActiveOpens)
-CREATE_LWIP_SYNC_NODE( 6, tcp_PassiveOpens)
-CREATE_LWIP_SYNC_NODE( 7, tcp_AttemptFails)
-CREATE_LWIP_SYNC_NODE( 8, tcp_EstabResets)
-CREATE_LWIP_SYNC_NODE( 9, tcp_CurrEstab)
-CREATE_LWIP_SYNC_NODE(10, tcp_InSegs)
-CREATE_LWIP_SYNC_NODE(11, tcp_OutSegs)
-CREATE_LWIP_SYNC_NODE(12, tcp_RetransSegs)
-#if LWIP_IPV4
-CREATE_LWIP_SYNC_NODE(13, tcp_ConnTable)
-#endif /* LWIP_IPV4 */
-CREATE_LWIP_SYNC_NODE(14, tcp_InErrs)
-CREATE_LWIP_SYNC_NODE(15, tcp_OutRsts)
-#if LWIP_HAVE_INT64
-CREATE_LWIP_SYNC_NODE(17, tcp_HCInSegs)
-CREATE_LWIP_SYNC_NODE(18, tcp_HCOutSegs)
-#endif
-CREATE_LWIP_SYNC_NODE(19, tcp_ConnectionTable)
-CREATE_LWIP_SYNC_NODE(20, tcp_ListenerTable)
-
-static const struct snmp_node *const tcp_nodes[] = {
-  &SYNC_NODE_NAME(tcp_RtoAlgorithm).node.node,
-  &SYNC_NODE_NAME(tcp_RtoMin).node.node,
-  &SYNC_NODE_NAME(tcp_RtoMax).node.node,
-  &SYNC_NODE_NAME(tcp_MaxConn).node.node,
-  &SYNC_NODE_NAME(tcp_ActiveOpens).node.node,
-  &SYNC_NODE_NAME(tcp_PassiveOpens).node.node,
-  &SYNC_NODE_NAME(tcp_AttemptFails).node.node,
-  &SYNC_NODE_NAME(tcp_EstabResets).node.node,
-  &SYNC_NODE_NAME(tcp_CurrEstab).node.node,
-  &SYNC_NODE_NAME(tcp_InSegs).node.node,
-  &SYNC_NODE_NAME(tcp_OutSegs).node.node,
-  &SYNC_NODE_NAME(tcp_RetransSegs).node.node,
-#if LWIP_IPV4
-  &SYNC_NODE_NAME(tcp_ConnTable).node.node,
-#endif /* LWIP_IPV4 */
-  &SYNC_NODE_NAME(tcp_InErrs).node.node,
-  &SYNC_NODE_NAME(tcp_OutRsts).node.node,
-  &SYNC_NODE_NAME(tcp_HCInSegs).node.node,
-#if LWIP_HAVE_INT64
-  &SYNC_NODE_NAME(tcp_HCOutSegs).node.node,
-  &SYNC_NODE_NAME(tcp_ConnectionTable).node.node,
-#endif
-  &SYNC_NODE_NAME(tcp_ListenerTable).node.node
-};
-
-const struct snmp_tree_node snmp_mib2_tcp_root = SNMP_CREATE_TREE_NODE(6, tcp_nodes);
-#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_TCP */
+/**
+ * @file
+ * Management Information Base II (RFC1213) TCP objects and functions.
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Dirk Ziegelmeier <dziegel@gmx.de>
+ *         Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#include "lwip/snmp.h"
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "lwip/apps/snmp_mib2.h"
+#include "lwip/apps/snmp_table.h"
+#include "lwip/apps/snmp_scalar.h"
+#include "lwip/tcp.h"
+#include "lwip/priv/tcp_priv.h"
+#include "lwip/stats.h"
+
+#include <string.h>
+
+#if LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_TCP
+
+#if SNMP_USE_NETCONN
+#define SYNC_NODE_NAME(node_name) node_name ## _synced
+#define CREATE_LWIP_SYNC_NODE(oid, node_name) \
+   static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks);
+#else
+#define SYNC_NODE_NAME(node_name) node_name
+#define CREATE_LWIP_SYNC_NODE(oid, node_name)
+#endif
+
+/* --- tcp .1.3.6.1.2.1.6 ----------------------------------------------------- */
+
+static s16_t
+tcp_get_value(struct snmp_node_instance* instance, void* value)
+{
+  u32_t *uint_ptr = (u32_t*)value;
+  s32_t *sint_ptr = (s32_t*)value;
+
+  switch (instance->node->oid) {
+  case 1: /* tcpRtoAlgorithm, vanj(4) */
+    *sint_ptr = 4;
+    return sizeof(*sint_ptr);
+  case 2: /* tcpRtoMin */
+    /* @todo not the actual value, a guess,
+        needs to be calculated */
+    *sint_ptr = 1000;
+    return sizeof(*sint_ptr);
+  case 3: /* tcpRtoMax */
+    /* @todo not the actual value, a guess,
+        needs to be calculated */
+    *sint_ptr = 60000;
+    return sizeof(*sint_ptr);
+  case 4: /* tcpMaxConn */
+    *sint_ptr = MEMP_NUM_TCP_PCB;
+    return sizeof(*sint_ptr);
+  case 5: /* tcpActiveOpens */
+    *uint_ptr = STATS_GET(mib2.tcpactiveopens);
+    return sizeof(*uint_ptr);
+  case 6: /* tcpPassiveOpens */
+    *uint_ptr = STATS_GET(mib2.tcppassiveopens);
+    return sizeof(*uint_ptr);
+  case 7: /* tcpAttemptFails */
+    *uint_ptr = STATS_GET(mib2.tcpattemptfails);
+    return sizeof(*uint_ptr);
+  case 8: /* tcpEstabResets */
+    *uint_ptr = STATS_GET(mib2.tcpestabresets);
+    return sizeof(*uint_ptr);
+  case 9: /* tcpCurrEstab */
+    {
+      u16_t tcpcurrestab = 0;
+      struct tcp_pcb *pcb = tcp_active_pcbs;
+      while (pcb != NULL) {
+        if ((pcb->state == ESTABLISHED) ||
+            (pcb->state == CLOSE_WAIT)) {
+          tcpcurrestab++;
+        }
+        pcb = pcb->next;
+      }
+      *uint_ptr = tcpcurrestab;
+    }
+    return sizeof(*uint_ptr);
+  case 10: /* tcpInSegs */
+    *uint_ptr = STATS_GET(mib2.tcpinsegs);
+    return sizeof(*uint_ptr);
+  case 11: /* tcpOutSegs */
+    *uint_ptr = STATS_GET(mib2.tcpoutsegs);
+    return sizeof(*uint_ptr);
+  case 12: /* tcpRetransSegs */
+    *uint_ptr = STATS_GET(mib2.tcpretranssegs);
+    return sizeof(*uint_ptr);
+  case 14: /* tcpInErrs */
+    *uint_ptr = STATS_GET(mib2.tcpinerrs);
+    return sizeof(*uint_ptr);
+  case 15: /* tcpOutRsts */
+    *uint_ptr = STATS_GET(mib2.tcpoutrsts);
+    return sizeof(*uint_ptr);
+  case 17: /* tcpHCInSegs */
+    memset(value, 0, 2*sizeof(u32_t)); /* not supported */
+    return 2*sizeof(u32_t);
+  case 18: /* tcpHCOutSegs */
+    memset(value, 0, 2*sizeof(u32_t)); /* not supported */
+    return 2*sizeof(u32_t);
+  default:
+    LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcp_get_value(): unknown id: %"S32_F"\n", instance->node->oid));
+    break;
+  }
+
+  return 0;
+}
+
+/* --- tcpConnTable --- */
+
+#if LWIP_IPV4
+
+/* list of allowed value ranges for incoming OID */
+static const struct snmp_oid_range tcp_ConnTable_oid_ranges[] = {
+  { 0, 0xff   }, /* IP A */
+  { 0, 0xff   }, /* IP B */
+  { 0, 0xff   }, /* IP C */
+  { 0, 0xff   }, /* IP D */
+  { 0, 0xffff }, /* Port */
+  { 0, 0xff   }, /* IP A */
+  { 0, 0xff   }, /* IP B */
+  { 0, 0xff   }, /* IP C */
+  { 0, 0xff   }, /* IP D */
+  { 0, 0xffff }  /* Port */
+};
+
+static snmp_err_t
+tcp_ConnTable_get_cell_value_core(struct tcp_pcb *pcb, const u32_t* column, union snmp_variant_value* value, u32_t* value_len)
+{
+  LWIP_UNUSED_ARG(value_len);
+
+  /* value */
+  switch (*column) {
+  case 1: /* tcpConnState */
+    value->u32 = pcb->state + 1;
+    break;
+  case 2: /* tcpConnLocalAddress */
+    value->u32 = ip_2_ip4(&pcb->local_ip)->addr;
+    break;
+  case 3: /* tcpConnLocalPort */
+    value->u32 = pcb->local_port;
+    break;
+  case 4: /* tcpConnRemAddress */
+    if (pcb->state == LISTEN) {
+      value->u32 = IP4_ADDR_ANY4->addr;
+    } else {
+      value->u32 = ip_2_ip4(&pcb->remote_ip)->addr;
+    }
+    break;
+  case 5: /* tcpConnRemPort */
+    if (pcb->state == LISTEN) {
+      value->u32 = 0;
+    } else {
+      value->u32 = pcb->remote_port;
+    }
+    break;
+  default:
+    LWIP_ASSERT("invalid id", 0);
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  return SNMP_ERR_NOERROR;
+}
+
+static snmp_err_t
+tcp_ConnTable_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len)
+{
+  u8_t i;
+  ip4_addr_t local_ip;
+  ip4_addr_t remote_ip;
+  u16_t local_port;
+  u16_t remote_port;
+  struct tcp_pcb *pcb;
+
+  /* check if incoming OID length and if values are in plausible range */
+  if (!snmp_oid_in_range(row_oid, row_oid_len, tcp_ConnTable_oid_ranges, LWIP_ARRAYSIZE(tcp_ConnTable_oid_ranges))) {
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  /* get IPs and ports from incoming OID */
+  snmp_oid_to_ip4(&row_oid[0], &local_ip); /* we know it succeeds because of oid_in_range check above */
+  local_port = (u16_t)row_oid[4];
+  snmp_oid_to_ip4(&row_oid[5], &remote_ip); /* we know it succeeds because of oid_in_range check above */
+  remote_port = (u16_t)row_oid[9];
+
+  /* find tcp_pcb with requested ips and ports */
+  for (i = 0; i < LWIP_ARRAYSIZE(tcp_pcb_lists); i++) {
+    pcb = *tcp_pcb_lists[i];
+
+    while (pcb != NULL) {
+      /* do local IP and local port match? */
+      if (IP_IS_V4_VAL(pcb->local_ip) &&
+         ip4_addr_cmp(&local_ip, ip_2_ip4(&pcb->local_ip)) && (local_port == pcb->local_port)) {
+
+        /* PCBs in state LISTEN are not connected and have no remote_ip or remote_port */
+        if (pcb->state == LISTEN) {
+          if (ip4_addr_cmp(&remote_ip, IP4_ADDR_ANY4) && (remote_port == 0)) {
+            /* fill in object properties */
+            return tcp_ConnTable_get_cell_value_core(pcb, column, value, value_len);
+          }
+        } else {
+          if (IP_IS_V4_VAL(pcb->remote_ip) &&
+             ip4_addr_cmp(&remote_ip, ip_2_ip4(&pcb->remote_ip)) && (remote_port == pcb->remote_port)) {
+            /* fill in object properties */
+            return tcp_ConnTable_get_cell_value_core(pcb, column, value, value_len);
+          }
+        }
+      }
+
+      pcb = pcb->next;
+    }
+  }
+
+  /* not found */
+  return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+static snmp_err_t
+tcp_ConnTable_get_next_cell_instance_and_value(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len)
+{
+  u8_t i;
+  struct tcp_pcb *pcb;
+  struct snmp_next_oid_state state;
+  u32_t result_temp[LWIP_ARRAYSIZE(tcp_ConnTable_oid_ranges)];
+
+  /* init struct to search next oid */
+  snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(tcp_ConnTable_oid_ranges));
+
+  /* iterate over all possible OIDs to find the next one */
+  for (i = 0; i < LWIP_ARRAYSIZE(tcp_pcb_lists); i++) {
+    pcb = *tcp_pcb_lists[i];
+    while (pcb != NULL) {
+      u32_t test_oid[LWIP_ARRAYSIZE(tcp_ConnTable_oid_ranges)];
+
+      if (IP_IS_V4_VAL(pcb->local_ip)) {
+        snmp_ip4_to_oid(ip_2_ip4(&pcb->local_ip), &test_oid[0]);
+        test_oid[4] = pcb->local_port;
+
+        /* PCBs in state LISTEN are not connected and have no remote_ip or remote_port */
+        if (pcb->state == LISTEN) {
+          snmp_ip4_to_oid(IP4_ADDR_ANY4, &test_oid[5]);
+          test_oid[9] = 0;
+        } else {
+          if (IP_IS_V6_VAL(pcb->remote_ip)) { /* should never happen */
+            continue;
+          }
+          snmp_ip4_to_oid(ip_2_ip4(&pcb->remote_ip), &test_oid[5]);
+          test_oid[9] = pcb->remote_port;
+        }
+
+        /* check generated OID: is it a candidate for the next one? */
+        snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(tcp_ConnTable_oid_ranges), pcb);
+      }
+
+      pcb = pcb->next;
+    }
+  }
+
+  /* did we find a next one? */
+  if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
+    snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
+    /* fill in object properties */
+    return tcp_ConnTable_get_cell_value_core((struct tcp_pcb*)state.reference, column, value, value_len);
+  }
+
+  /* not found */
+  return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+#endif /* LWIP_IPV4 */
+
+/* --- tcpConnectionTable --- */
+
+static snmp_err_t
+tcp_ConnectionTable_get_cell_value_core(const u32_t* column, struct tcp_pcb *pcb, union snmp_variant_value* value)
+{
+  /* all items except tcpConnectionState and tcpConnectionProcess are declared as not-accessible */
+  switch (*column) {
+  case 7: /* tcpConnectionState */
+    value->u32 = pcb->state + 1;
+    break;
+  case 8: /* tcpConnectionProcess */
+    value->u32 = 0; /* not supported */
+    break;
+  default:
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  return SNMP_ERR_NOERROR;
+}
+
+static snmp_err_t
+tcp_ConnectionTable_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len)
+{
+  ip_addr_t local_ip, remote_ip;
+  u16_t local_port, remote_port;
+  struct tcp_pcb *pcb;
+  u8_t idx = 0;
+  u8_t i;
+  struct tcp_pcb ** const tcp_pcb_nonlisten_lists[] = {&tcp_bound_pcbs, &tcp_active_pcbs, &tcp_tw_pcbs};
+
+  LWIP_UNUSED_ARG(value_len);
+
+  /* tcpConnectionLocalAddressType + tcpConnectionLocalAddress + tcpConnectionLocalPort */
+  idx += snmp_oid_to_ip_port(&row_oid[idx], row_oid_len-idx, &local_ip, &local_port);
+  if (idx == 0) {
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  /* tcpConnectionRemAddressType + tcpConnectionRemAddress + tcpConnectionRemPort */
+  idx += snmp_oid_to_ip_port(&row_oid[idx], row_oid_len-idx, &remote_ip, &remote_port);
+  if (idx == 0) {
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  /* find tcp_pcb with requested ip and port*/
+  for (i = 0; i < LWIP_ARRAYSIZE(tcp_pcb_nonlisten_lists); i++) {
+    pcb = *tcp_pcb_nonlisten_lists[i];
+
+    while (pcb != NULL) {
+      if (ip_addr_cmp(&local_ip, &pcb->local_ip) &&
+         (local_port == pcb->local_port) &&
+         ip_addr_cmp(&remote_ip, &pcb->remote_ip) &&
+         (remote_port == pcb->remote_port)) {
+        /* fill in object properties */
+        return tcp_ConnectionTable_get_cell_value_core(column, pcb, value);
+      }
+      pcb = pcb->next;
+    }
+  }
+
+  /* not found */
+  return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+static snmp_err_t
+tcp_ConnectionTable_get_next_cell_instance_and_value(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len)
+{
+  struct tcp_pcb *pcb;
+  struct snmp_next_oid_state state;
+  /* 1x tcpConnectionLocalAddressType + 1x OID len + 16x tcpConnectionLocalAddress  + 1x tcpConnectionLocalPort
+   * 1x tcpConnectionRemAddressType   + 1x OID len + 16x tcpConnectionRemAddress    + 1x tcpConnectionRemPort */
+  u32_t  result_temp[38];
+  u8_t i;
+  struct tcp_pcb ** const tcp_pcb_nonlisten_lists[] = {&tcp_bound_pcbs, &tcp_active_pcbs, &tcp_tw_pcbs};
+
+  LWIP_UNUSED_ARG(value_len);
+
+  /* init struct to search next oid */
+  snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(result_temp));
+
+  /* iterate over all possible OIDs to find the next one */
+  for (i = 0; i < LWIP_ARRAYSIZE(tcp_pcb_nonlisten_lists); i++) {
+    pcb = *tcp_pcb_nonlisten_lists[i];
+
+    while (pcb != NULL) {
+      u8_t idx = 0;
+      u32_t test_oid[LWIP_ARRAYSIZE(result_temp)];
+
+      /* tcpConnectionLocalAddressType + tcpConnectionLocalAddress + tcpConnectionLocalPort */
+      idx += snmp_ip_port_to_oid(&pcb->local_ip, pcb->local_port, &test_oid[idx]);
+
+      /* tcpConnectionRemAddressType + tcpConnectionRemAddress + tcpConnectionRemPort */
+      idx += snmp_ip_port_to_oid(&pcb->remote_ip, pcb->remote_port, &test_oid[idx]);
+
+      /* check generated OID: is it a candidate for the next one? */
+      snmp_next_oid_check(&state, test_oid, idx, pcb);
+
+      pcb = pcb->next;
+    }
+  }
+
+  /* did we find a next one? */
+  if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
+    snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
+    /* fill in object properties */
+    return tcp_ConnectionTable_get_cell_value_core(column, (struct tcp_pcb*)state.reference, value);
+  } else {
+    /* not found */
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+}
+
+/* --- tcpListenerTable --- */
+
+static snmp_err_t
+tcp_ListenerTable_get_cell_value_core(const u32_t* column, union snmp_variant_value* value)
+{
+  /* all items except tcpListenerProcess are declared as not-accessible */
+  switch (*column) {
+  case 4: /* tcpListenerProcess */
+    value->u32 = 0; /* not supported */
+    break;
+  default:
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  return SNMP_ERR_NOERROR;
+}
+
+static snmp_err_t
+tcp_ListenerTable_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len)
+{
+  ip_addr_t local_ip;
+  u16_t local_port;
+  struct tcp_pcb_listen *pcb;
+  u8_t idx = 0;
+
+  LWIP_UNUSED_ARG(value_len);
+
+  /* tcpListenerLocalAddressType + tcpListenerLocalAddress + tcpListenerLocalPort */
+  idx += snmp_oid_to_ip_port(&row_oid[idx], row_oid_len-idx, &local_ip, &local_port);
+  if (idx == 0) {
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  /* find tcp_pcb with requested ip and port*/
+  pcb = tcp_listen_pcbs.listen_pcbs;
+  while (pcb != NULL) {
+    if (ip_addr_cmp(&local_ip, &pcb->local_ip) &&
+       (local_port == pcb->local_port)) {
+      /* fill in object properties */
+      return tcp_ListenerTable_get_cell_value_core(column, value);
+    }
+    pcb = pcb->next;
+  }
+
+  /* not found */
+  return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+static snmp_err_t
+tcp_ListenerTable_get_next_cell_instance_and_value(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len)
+{
+  struct tcp_pcb_listen *pcb;
+  struct snmp_next_oid_state state;
+  /* 1x tcpListenerLocalAddressType + 1x OID len + 16x tcpListenerLocalAddress  + 1x tcpListenerLocalPort */
+  u32_t  result_temp[19];
+
+  LWIP_UNUSED_ARG(value_len);
+
+  /* init struct to search next oid */
+  snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(result_temp));
+
+  /* iterate over all possible OIDs to find the next one */
+  pcb = tcp_listen_pcbs.listen_pcbs;
+  while (pcb != NULL) {
+    u8_t idx = 0;
+    u32_t test_oid[LWIP_ARRAYSIZE(result_temp)];
+
+    /* tcpListenerLocalAddressType + tcpListenerLocalAddress + tcpListenerLocalPort */
+    idx += snmp_ip_port_to_oid(&pcb->local_ip, pcb->local_port, &test_oid[idx]);
+
+    /* check generated OID: is it a candidate for the next one? */
+    snmp_next_oid_check(&state, test_oid, idx, NULL);
+
+    pcb = pcb->next;
+  }
+
+  /* did we find a next one? */
+  if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
+    snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
+    /* fill in object properties */
+    return tcp_ListenerTable_get_cell_value_core(column, value);
+  } else {
+    /* not found */
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+}
+
+static const struct snmp_scalar_node tcp_RtoAlgorithm  = SNMP_SCALAR_CREATE_NODE_READONLY(1, SNMP_ASN1_TYPE_INTEGER, tcp_get_value);
+static const struct snmp_scalar_node tcp_RtoMin        = SNMP_SCALAR_CREATE_NODE_READONLY(2, SNMP_ASN1_TYPE_INTEGER, tcp_get_value);
+static const struct snmp_scalar_node tcp_RtoMax        = SNMP_SCALAR_CREATE_NODE_READONLY(3, SNMP_ASN1_TYPE_INTEGER, tcp_get_value);
+static const struct snmp_scalar_node tcp_MaxConn       = SNMP_SCALAR_CREATE_NODE_READONLY(4, SNMP_ASN1_TYPE_INTEGER, tcp_get_value);
+static const struct snmp_scalar_node tcp_ActiveOpens   = SNMP_SCALAR_CREATE_NODE_READONLY(5, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
+static const struct snmp_scalar_node tcp_PassiveOpens  = SNMP_SCALAR_CREATE_NODE_READONLY(6, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
+static const struct snmp_scalar_node tcp_AttemptFails  = SNMP_SCALAR_CREATE_NODE_READONLY(7, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
+static const struct snmp_scalar_node tcp_EstabResets   = SNMP_SCALAR_CREATE_NODE_READONLY(8, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
+static const struct snmp_scalar_node tcp_CurrEstab     = SNMP_SCALAR_CREATE_NODE_READONLY(9, SNMP_ASN1_TYPE_GAUGE, tcp_get_value);
+static const struct snmp_scalar_node tcp_InSegs        = SNMP_SCALAR_CREATE_NODE_READONLY(10, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
+static const struct snmp_scalar_node tcp_OutSegs       = SNMP_SCALAR_CREATE_NODE_READONLY(11, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
+static const struct snmp_scalar_node tcp_RetransSegs   = SNMP_SCALAR_CREATE_NODE_READONLY(12, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
+static const struct snmp_scalar_node tcp_InErrs        = SNMP_SCALAR_CREATE_NODE_READONLY(14, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
+static const struct snmp_scalar_node tcp_OutRsts       = SNMP_SCALAR_CREATE_NODE_READONLY(15, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
+static const struct snmp_scalar_node tcp_HCInSegs      = SNMP_SCALAR_CREATE_NODE_READONLY(17, SNMP_ASN1_TYPE_COUNTER64, tcp_get_value);
+static const struct snmp_scalar_node tcp_HCOutSegs     = SNMP_SCALAR_CREATE_NODE_READONLY(18, SNMP_ASN1_TYPE_COUNTER64, tcp_get_value);
+
+#if LWIP_IPV4
+static const struct snmp_table_simple_col_def tcp_ConnTable_columns[] = {
+  {  1, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* tcpConnState */
+  {  2, SNMP_ASN1_TYPE_IPADDR,  SNMP_VARIANT_VALUE_TYPE_U32 }, /* tcpConnLocalAddress */
+  {  3, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* tcpConnLocalPort */
+  {  4, SNMP_ASN1_TYPE_IPADDR,  SNMP_VARIANT_VALUE_TYPE_U32 }, /* tcpConnRemAddress */
+  {  5, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }  /* tcpConnRemPort */
+};
+
+static const struct snmp_table_simple_node tcp_ConnTable = SNMP_TABLE_CREATE_SIMPLE(13, tcp_ConnTable_columns, tcp_ConnTable_get_cell_value, tcp_ConnTable_get_next_cell_instance_and_value);
+#endif /* LWIP_IPV4 */
+
+static const struct snmp_table_simple_col_def tcp_ConnectionTable_columns[] = {
+  /* all items except tcpConnectionState and tcpConnectionProcess are declared as not-accessible */
+  { 7, SNMP_ASN1_TYPE_INTEGER,    SNMP_VARIANT_VALUE_TYPE_U32 }, /* tcpConnectionState */
+  { 8, SNMP_ASN1_TYPE_UNSIGNED32, SNMP_VARIANT_VALUE_TYPE_U32 }  /* tcpConnectionProcess */
+};
+
+static const struct snmp_table_simple_node tcp_ConnectionTable = SNMP_TABLE_CREATE_SIMPLE(19, tcp_ConnectionTable_columns, tcp_ConnectionTable_get_cell_value, tcp_ConnectionTable_get_next_cell_instance_and_value);
+
+
+static const struct snmp_table_simple_col_def tcp_ListenerTable_columns[] = {
+  /* all items except tcpListenerProcess are declared as not-accessible */
+  { 4, SNMP_ASN1_TYPE_UNSIGNED32, SNMP_VARIANT_VALUE_TYPE_U32 }  /* tcpListenerProcess */
+};
+
+static const struct snmp_table_simple_node tcp_ListenerTable = SNMP_TABLE_CREATE_SIMPLE(20, tcp_ListenerTable_columns, tcp_ListenerTable_get_cell_value, tcp_ListenerTable_get_next_cell_instance_and_value);
+
+/* the following nodes access variables in LWIP stack from SNMP worker thread and must therefore be synced to LWIP (TCPIP) thread */
+CREATE_LWIP_SYNC_NODE( 1, tcp_RtoAlgorithm)
+CREATE_LWIP_SYNC_NODE( 2, tcp_RtoMin)
+CREATE_LWIP_SYNC_NODE( 3, tcp_RtoMax)
+CREATE_LWIP_SYNC_NODE( 4, tcp_MaxConn)
+CREATE_LWIP_SYNC_NODE( 5, tcp_ActiveOpens)
+CREATE_LWIP_SYNC_NODE( 6, tcp_PassiveOpens)
+CREATE_LWIP_SYNC_NODE( 7, tcp_AttemptFails)
+CREATE_LWIP_SYNC_NODE( 8, tcp_EstabResets)
+CREATE_LWIP_SYNC_NODE( 9, tcp_CurrEstab)
+CREATE_LWIP_SYNC_NODE(10, tcp_InSegs)
+CREATE_LWIP_SYNC_NODE(11, tcp_OutSegs)
+CREATE_LWIP_SYNC_NODE(12, tcp_RetransSegs)
+#if LWIP_IPV4
+CREATE_LWIP_SYNC_NODE(13, tcp_ConnTable)
+#endif /* LWIP_IPV4 */
+CREATE_LWIP_SYNC_NODE(14, tcp_InErrs)
+CREATE_LWIP_SYNC_NODE(15, tcp_OutRsts)
+CREATE_LWIP_SYNC_NODE(17, tcp_HCInSegs)
+CREATE_LWIP_SYNC_NODE(18, tcp_HCOutSegs)
+CREATE_LWIP_SYNC_NODE(19, tcp_ConnectionTable)
+CREATE_LWIP_SYNC_NODE(20, tcp_ListenerTable)
+
+static const struct snmp_node* const tcp_nodes[] = {
+  &SYNC_NODE_NAME(tcp_RtoAlgorithm).node.node,
+  &SYNC_NODE_NAME(tcp_RtoMin).node.node,
+  &SYNC_NODE_NAME(tcp_RtoMax).node.node,
+  &SYNC_NODE_NAME(tcp_MaxConn).node.node,
+  &SYNC_NODE_NAME(tcp_ActiveOpens).node.node,
+  &SYNC_NODE_NAME(tcp_PassiveOpens).node.node,
+  &SYNC_NODE_NAME(tcp_AttemptFails).node.node,
+  &SYNC_NODE_NAME(tcp_EstabResets).node.node,
+  &SYNC_NODE_NAME(tcp_CurrEstab).node.node,
+  &SYNC_NODE_NAME(tcp_InSegs).node.node,
+  &SYNC_NODE_NAME(tcp_OutSegs).node.node,
+  &SYNC_NODE_NAME(tcp_RetransSegs).node.node,
+#if LWIP_IPV4
+  &SYNC_NODE_NAME(tcp_ConnTable).node.node,
+#endif /* LWIP_IPV4 */
+  &SYNC_NODE_NAME(tcp_InErrs).node.node,
+  &SYNC_NODE_NAME(tcp_OutRsts).node.node,
+  &SYNC_NODE_NAME(tcp_HCInSegs).node.node,
+  &SYNC_NODE_NAME(tcp_HCOutSegs).node.node,
+  &SYNC_NODE_NAME(tcp_ConnectionTable).node.node,
+  &SYNC_NODE_NAME(tcp_ListenerTable).node.node
+};
+
+const struct snmp_tree_node snmp_mib2_tcp_root = SNMP_CREATE_TREE_NODE(6, tcp_nodes);
+#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_TCP */

+ 357 - 372
libs/thirdparty/lwip_2.1.2/src/apps/snmp/snmp_mib2_udp.c → libs/thirdparty/LwIP/src/apps/snmp/snmp_mib2_udp.c

@@ -1,372 +1,357 @@
-/**
- * @file
- * Management Information Base II (RFC1213) UDP objects and functions.
- */
-
-/*
- * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * Author: Dirk Ziegelmeier <dziegel@gmx.de>
- *         Christiaan Simons <christiaan.simons@axon.tv>
- */
-
-#include "lwip/snmp.h"
-#include "lwip/apps/snmp.h"
-#include "lwip/apps/snmp_core.h"
-#include "lwip/apps/snmp_mib2.h"
-#include "lwip/apps/snmp_table.h"
-#include "lwip/apps/snmp_scalar.h"
-#include "lwip/udp.h"
-#include "lwip/stats.h"
-
-#include <string.h>
-
-#if LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_UDP
-
-#if SNMP_USE_NETCONN
-#define SYNC_NODE_NAME(node_name) node_name ## _synced
-#define CREATE_LWIP_SYNC_NODE(oid, node_name) \
-   static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks);
-#else
-#define SYNC_NODE_NAME(node_name) node_name
-#define CREATE_LWIP_SYNC_NODE(oid, node_name)
-#endif
-
-/* --- udp .1.3.6.1.2.1.7 ----------------------------------------------------- */
-
-static s16_t
-udp_get_value(struct snmp_node_instance *instance, void *value)
-{
-  u32_t *uint_ptr = (u32_t *)value;
-
-  switch (instance->node->oid) {
-    case 1: /* udpInDatagrams */
-      *uint_ptr = STATS_GET(mib2.udpindatagrams);
-      return sizeof(*uint_ptr);
-    case 2: /* udpNoPorts */
-      *uint_ptr = STATS_GET(mib2.udpnoports);
-      return sizeof(*uint_ptr);
-    case 3: /* udpInErrors */
-      *uint_ptr = STATS_GET(mib2.udpinerrors);
-      return sizeof(*uint_ptr);
-    case 4: /* udpOutDatagrams */
-      *uint_ptr = STATS_GET(mib2.udpoutdatagrams);
-      return sizeof(*uint_ptr);
-#if LWIP_HAVE_INT64
-    case 8: { /* udpHCInDatagrams */
-      /* use the 32 bit counter for now... */
-      u64_t val64 = STATS_GET(mib2.udpindatagrams);
-      *((u64_t *)value) = val64;
-    }
-    return sizeof(u64_t);
-    case 9: { /* udpHCOutDatagrams */
-      /* use the 32 bit counter for now... */
-      u64_t val64 = STATS_GET(mib2.udpoutdatagrams);
-      *((u64_t *)value) = val64;
-    }
-    return sizeof(u64_t);
-#endif
-    default:
-      LWIP_DEBUGF(SNMP_MIB_DEBUG, ("udp_get_value(): unknown id: %"S32_F"\n", instance->node->oid));
-      break;
-  }
-
-  return 0;
-}
-
-/* --- udpEndpointTable --- */
-
-static snmp_err_t
-udp_endpointTable_get_cell_value_core(const u32_t *column, union snmp_variant_value *value)
-{
-  /* all items except udpEndpointProcess are declared as not-accessible */
-  switch (*column) {
-    case 8: /* udpEndpointProcess */
-      value->u32 = 0; /* not supported */
-      break;
-    default:
-      return SNMP_ERR_NOSUCHINSTANCE;
-  }
-
-  return SNMP_ERR_NOERROR;
-}
-
-static snmp_err_t
-udp_endpointTable_get_cell_value(const u32_t *column, const u32_t *row_oid, u8_t row_oid_len, union snmp_variant_value *value, u32_t *value_len)
-{
-  ip_addr_t local_ip, remote_ip;
-  u16_t local_port, remote_port;
-  struct udp_pcb *pcb;
-  u8_t idx = 0;
-
-  LWIP_UNUSED_ARG(value_len);
-
-  /* udpEndpointLocalAddressType + udpEndpointLocalAddress + udpEndpointLocalPort */
-  idx += snmp_oid_to_ip_port(&row_oid[idx], row_oid_len - idx, &local_ip, &local_port);
-  if (idx == 0) {
-    return SNMP_ERR_NOSUCHINSTANCE;
-  }
-
-  /* udpEndpointRemoteAddressType + udpEndpointRemoteAddress + udpEndpointRemotePort */
-  idx += snmp_oid_to_ip_port(&row_oid[idx], row_oid_len - idx, &remote_ip, &remote_port);
-  if (idx == 0) {
-    return SNMP_ERR_NOSUCHINSTANCE;
-  }
-
-  /* udpEndpointInstance */
-  if (row_oid_len < (idx + 1)) {
-    return SNMP_ERR_NOSUCHINSTANCE;
-  }
-  if (row_oid[idx] != 0) {
-    return SNMP_ERR_NOSUCHINSTANCE;
-  }
-
-  /* find udp_pcb with requested ip and port*/
-  pcb = udp_pcbs;
-  while (pcb != NULL) {
-    if (ip_addr_cmp(&local_ip, &pcb->local_ip) &&
-        (local_port == pcb->local_port) &&
-        ip_addr_cmp(&remote_ip, &pcb->remote_ip) &&
-        (remote_port == pcb->remote_port)) {
-      /* fill in object properties */
-      return udp_endpointTable_get_cell_value_core(column, value);
-    }
-    pcb = pcb->next;
-  }
-
-  /* not found */
-  return SNMP_ERR_NOSUCHINSTANCE;
-}
-
-static snmp_err_t
-udp_endpointTable_get_next_cell_instance_and_value(const u32_t *column, struct snmp_obj_id *row_oid, union snmp_variant_value *value, u32_t *value_len)
-{
-  struct udp_pcb *pcb;
-  struct snmp_next_oid_state state;
-  /* 1x udpEndpointLocalAddressType  + 1x OID len + 16x udpEndpointLocalAddress  + 1x udpEndpointLocalPort  +
-   * 1x udpEndpointRemoteAddressType + 1x OID len + 16x udpEndpointRemoteAddress + 1x udpEndpointRemotePort +
-   * 1x udpEndpointInstance = 39
-   */
-  u32_t  result_temp[39];
-
-  LWIP_UNUSED_ARG(value_len);
-
-  /* init struct to search next oid */
-  snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(result_temp));
-
-  /* iterate over all possible OIDs to find the next one */
-  pcb = udp_pcbs;
-  while (pcb != NULL) {
-    u32_t test_oid[LWIP_ARRAYSIZE(result_temp)];
-    u8_t idx = 0;
-
-    /* udpEndpointLocalAddressType + udpEndpointLocalAddress + udpEndpointLocalPort */
-    idx += snmp_ip_port_to_oid(&pcb->local_ip, pcb->local_port, &test_oid[idx]);
-
-    /* udpEndpointRemoteAddressType + udpEndpointRemoteAddress + udpEndpointRemotePort */
-    idx += snmp_ip_port_to_oid(&pcb->remote_ip, pcb->remote_port, &test_oid[idx]);
-
-    test_oid[idx] = 0; /* udpEndpointInstance */
-    idx++;
-
-    /* check generated OID: is it a candidate for the next one? */
-    snmp_next_oid_check(&state, test_oid, idx, NULL);
-
-    pcb = pcb->next;
-  }
-
-  /* did we find a next one? */
-  if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
-    snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
-    /* fill in object properties */
-    return udp_endpointTable_get_cell_value_core(column, value);
-  } else {
-    /* not found */
-    return SNMP_ERR_NOSUCHINSTANCE;
-  }
-}
-
-/* --- udpTable --- */
-
-#if LWIP_IPV4
-
-/* list of allowed value ranges for incoming OID */
-static const struct snmp_oid_range udp_Table_oid_ranges[] = {
-  { 0, 0xff   }, /* IP A        */
-  { 0, 0xff   }, /* IP B        */
-  { 0, 0xff   }, /* IP C        */
-  { 0, 0xff   }, /* IP D        */
-  { 1, 0xffff }  /* Port        */
-};
-
-static snmp_err_t
-udp_Table_get_cell_value_core(struct udp_pcb *pcb, const u32_t *column, union snmp_variant_value *value, u32_t *value_len)
-{
-  LWIP_UNUSED_ARG(value_len);
-
-  switch (*column) {
-    case 1: /* udpLocalAddress */
-      /* set reference to PCB local IP and return a generic node that copies IP4 addresses */
-      value->u32 = ip_2_ip4(&pcb->local_ip)->addr;
-      break;
-    case 2: /* udpLocalPort */
-      /* set reference to PCB local port and return a generic node that copies u16_t values */
-      value->u32 = pcb->local_port;
-      break;
-    default:
-      return SNMP_ERR_NOSUCHINSTANCE;
-  }
-
-  return SNMP_ERR_NOERROR;
-}
-
-static snmp_err_t
-udp_Table_get_cell_value(const u32_t *column, const u32_t *row_oid, u8_t row_oid_len, union snmp_variant_value *value, u32_t *value_len)
-{
-  ip4_addr_t ip;
-  u16_t port;
-  struct udp_pcb *pcb;
-
-  /* check if incoming OID length and if values are in plausible range */
-  if (!snmp_oid_in_range(row_oid, row_oid_len, udp_Table_oid_ranges, LWIP_ARRAYSIZE(udp_Table_oid_ranges))) {
-    return SNMP_ERR_NOSUCHINSTANCE;
-  }
-
-  /* get IP and port from incoming OID */
-  snmp_oid_to_ip4(&row_oid[0], &ip); /* we know it succeeds because of oid_in_range check above */
-  port = (u16_t)row_oid[4];
-
-  /* find udp_pcb with requested ip and port*/
-  pcb = udp_pcbs;
-  while (pcb != NULL) {
-    if (IP_IS_V4_VAL(pcb->local_ip)) {
-      if (ip4_addr_cmp(&ip, ip_2_ip4(&pcb->local_ip)) && (port == pcb->local_port)) {
-        /* fill in object properties */
-        return udp_Table_get_cell_value_core(pcb, column, value, value_len);
-      }
-    }
-    pcb = pcb->next;
-  }
-
-  /* not found */
-  return SNMP_ERR_NOSUCHINSTANCE;
-}
-
-static snmp_err_t
-udp_Table_get_next_cell_instance_and_value(const u32_t *column, struct snmp_obj_id *row_oid, union snmp_variant_value *value, u32_t *value_len)
-{
-  struct udp_pcb *pcb;
-  struct snmp_next_oid_state state;
-  u32_t  result_temp[LWIP_ARRAYSIZE(udp_Table_oid_ranges)];
-
-  /* init struct to search next oid */
-  snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(udp_Table_oid_ranges));
-
-  /* iterate over all possible OIDs to find the next one */
-  pcb = udp_pcbs;
-  while (pcb != NULL) {
-    u32_t test_oid[LWIP_ARRAYSIZE(udp_Table_oid_ranges)];
-
-    if (IP_IS_V4_VAL(pcb->local_ip)) {
-      snmp_ip4_to_oid(ip_2_ip4(&pcb->local_ip), &test_oid[0]);
-      test_oid[4] = pcb->local_port;
-
-      /* check generated OID: is it a candidate for the next one? */
-      snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(udp_Table_oid_ranges), pcb);
-    }
-
-    pcb = pcb->next;
-  }
-
-  /* did we find a next one? */
-  if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
-    snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
-    /* fill in object properties */
-    return udp_Table_get_cell_value_core((struct udp_pcb *)state.reference, column, value, value_len);
-  } else {
-    /* not found */
-    return SNMP_ERR_NOSUCHINSTANCE;
-  }
-}
-
-#endif /* LWIP_IPV4 */
-
-static const struct snmp_scalar_node udp_inDatagrams    = SNMP_SCALAR_CREATE_NODE_READONLY(1, SNMP_ASN1_TYPE_COUNTER,   udp_get_value);
-static const struct snmp_scalar_node udp_noPorts        = SNMP_SCALAR_CREATE_NODE_READONLY(2, SNMP_ASN1_TYPE_COUNTER,   udp_get_value);
-static const struct snmp_scalar_node udp_inErrors       = SNMP_SCALAR_CREATE_NODE_READONLY(3, SNMP_ASN1_TYPE_COUNTER,   udp_get_value);
-static const struct snmp_scalar_node udp_outDatagrams   = SNMP_SCALAR_CREATE_NODE_READONLY(4, SNMP_ASN1_TYPE_COUNTER,   udp_get_value);
-#if LWIP_HAVE_INT64
-static const struct snmp_scalar_node udp_HCInDatagrams  = SNMP_SCALAR_CREATE_NODE_READONLY(8, SNMP_ASN1_TYPE_COUNTER64, udp_get_value);
-static const struct snmp_scalar_node udp_HCOutDatagrams = SNMP_SCALAR_CREATE_NODE_READONLY(9, SNMP_ASN1_TYPE_COUNTER64, udp_get_value);
-#endif
-
-#if LWIP_IPV4
-static const struct snmp_table_simple_col_def udp_Table_columns[] = {
-  { 1, SNMP_ASN1_TYPE_IPADDR,  SNMP_VARIANT_VALUE_TYPE_U32 }, /* udpLocalAddress */
-  { 2, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }  /* udpLocalPort */
-};
-static const struct snmp_table_simple_node udp_Table = SNMP_TABLE_CREATE_SIMPLE(5, udp_Table_columns, udp_Table_get_cell_value, udp_Table_get_next_cell_instance_and_value);
-#endif /* LWIP_IPV4 */
-
-static const struct snmp_table_simple_col_def udp_endpointTable_columns[] = {
-  /* all items except udpEndpointProcess are declared as not-accessible */
-  { 8, SNMP_ASN1_TYPE_UNSIGNED32, SNMP_VARIANT_VALUE_TYPE_U32 }  /* udpEndpointProcess */
-};
-
-static const struct snmp_table_simple_node udp_endpointTable = SNMP_TABLE_CREATE_SIMPLE(7, udp_endpointTable_columns, udp_endpointTable_get_cell_value, udp_endpointTable_get_next_cell_instance_and_value);
-
-/* the following nodes access variables in LWIP stack from SNMP worker thread and must therefore be synced to LWIP (TCPIP) thread */
-CREATE_LWIP_SYNC_NODE(1, udp_inDatagrams)
-CREATE_LWIP_SYNC_NODE(2, udp_noPorts)
-CREATE_LWIP_SYNC_NODE(3, udp_inErrors)
-CREATE_LWIP_SYNC_NODE(4, udp_outDatagrams)
-#if LWIP_IPV4
-CREATE_LWIP_SYNC_NODE(5, udp_Table)
-#endif /* LWIP_IPV4 */
-CREATE_LWIP_SYNC_NODE(7, udp_endpointTable)
-#if LWIP_HAVE_INT64
-CREATE_LWIP_SYNC_NODE(8, udp_HCInDatagrams)
-CREATE_LWIP_SYNC_NODE(9, udp_HCOutDatagrams)
-#endif
-
-static const struct snmp_node *const udp_nodes[] = {
-  &SYNC_NODE_NAME(udp_inDatagrams).node.node,
-  &SYNC_NODE_NAME(udp_noPorts).node.node,
-  &SYNC_NODE_NAME(udp_inErrors).node.node,
-  &SYNC_NODE_NAME(udp_outDatagrams).node.node,
-#if LWIP_IPV4
-  &SYNC_NODE_NAME(udp_Table).node.node,
-#endif /* LWIP_IPV4 */
-  &SYNC_NODE_NAME(udp_endpointTable).node.node
-#if LWIP_HAVE_INT64
-  ,
-  &SYNC_NODE_NAME(udp_HCInDatagrams).node.node,
-  &SYNC_NODE_NAME(udp_HCOutDatagrams).node.node
-#endif
-};
-
-const struct snmp_tree_node snmp_mib2_udp_root = SNMP_CREATE_TREE_NODE(7, udp_nodes);
-#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_UDP */
+/**
+ * @file
+ * Management Information Base II (RFC1213) UDP objects and functions.
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Dirk Ziegelmeier <dziegel@gmx.de>
+ *         Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#include "lwip/snmp.h"
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "lwip/apps/snmp_mib2.h"
+#include "lwip/apps/snmp_table.h"
+#include "lwip/apps/snmp_scalar.h"
+#include "lwip/udp.h"
+#include "lwip/stats.h"
+
+#include <string.h>
+
+#if LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_UDP
+
+#if SNMP_USE_NETCONN
+#define SYNC_NODE_NAME(node_name) node_name ## _synced
+#define CREATE_LWIP_SYNC_NODE(oid, node_name) \
+   static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks);
+#else
+#define SYNC_NODE_NAME(node_name) node_name
+#define CREATE_LWIP_SYNC_NODE(oid, node_name)
+#endif
+
+/* --- udp .1.3.6.1.2.1.7 ----------------------------------------------------- */
+
+static s16_t
+udp_get_value(struct snmp_node_instance* instance, void* value)
+{
+  u32_t *uint_ptr = (u32_t*)value;
+
+  switch (instance->node->oid) {
+  case 1: /* udpInDatagrams */
+    *uint_ptr = STATS_GET(mib2.udpindatagrams);
+    return sizeof(*uint_ptr);
+  case 2: /* udpNoPorts */
+    *uint_ptr = STATS_GET(mib2.udpnoports);
+    return sizeof(*uint_ptr);
+  case 3: /* udpInErrors */
+    *uint_ptr = STATS_GET(mib2.udpinerrors);
+    return sizeof(*uint_ptr);
+  case 4: /* udpOutDatagrams */
+    *uint_ptr = STATS_GET(mib2.udpoutdatagrams);
+    return sizeof(*uint_ptr);
+  case 8: /* udpHCInDatagrams */
+    memset(value, 0, 2*sizeof(u32_t)); /* not supported */
+    return 2*sizeof(u32_t);
+  case 9: /* udpHCOutDatagrams */
+    memset(value, 0, 2*sizeof(u32_t)); /* not supported */
+    return 2*sizeof(u32_t);
+  default:
+    LWIP_DEBUGF(SNMP_MIB_DEBUG,("udp_get_value(): unknown id: %"S32_F"\n", instance->node->oid));
+    break;
+  }
+
+  return 0;
+}
+
+/* --- udpEndpointTable --- */
+
+static snmp_err_t
+udp_endpointTable_get_cell_value_core(const u32_t* column, union snmp_variant_value* value)
+{
+  /* all items except udpEndpointProcess are declared as not-accessible */
+  switch (*column) {
+  case 8: /* udpEndpointProcess */
+    value->u32 = 0; /* not supported */
+    break;
+  default:
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  return SNMP_ERR_NOERROR;
+}
+
+static snmp_err_t
+udp_endpointTable_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len)
+{
+  ip_addr_t local_ip, remote_ip;
+  u16_t local_port, remote_port;
+  struct udp_pcb *pcb;
+  u8_t idx = 0;
+
+  LWIP_UNUSED_ARG(value_len);
+
+  /* udpEndpointLocalAddressType + udpEndpointLocalAddress + udpEndpointLocalPort */
+  idx += snmp_oid_to_ip_port(&row_oid[idx], row_oid_len-idx, &local_ip, &local_port);
+  if (idx == 0) {
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  /* udpEndpointRemoteAddressType + udpEndpointRemoteAddress + udpEndpointRemotePort */
+  idx += snmp_oid_to_ip_port(&row_oid[idx], row_oid_len-idx, &remote_ip, &remote_port);
+  if (idx == 0) {
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  /* udpEndpointInstance */
+  if (row_oid_len < (idx+1)) {
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+  if (row_oid[idx] != 0) {
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+  
+  /* find udp_pcb with requested ip and port*/
+  pcb = udp_pcbs;
+  while (pcb != NULL) {
+    if (ip_addr_cmp(&local_ip, &pcb->local_ip) &&
+       (local_port == pcb->local_port) &&
+       ip_addr_cmp(&remote_ip, &pcb->remote_ip) &&
+       (remote_port == pcb->remote_port)) {
+      /* fill in object properties */
+      return udp_endpointTable_get_cell_value_core(column, value);
+    }
+    pcb = pcb->next;
+  }
+
+  /* not found */
+  return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+static snmp_err_t 
+udp_endpointTable_get_next_cell_instance_and_value(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len)
+{
+  struct udp_pcb *pcb;
+  struct snmp_next_oid_state state;
+  /* 1x udpEndpointLocalAddressType  + 1x OID len + 16x udpEndpointLocalAddress  + 1x udpEndpointLocalPort  +
+   * 1x udpEndpointRemoteAddressType + 1x OID len + 16x udpEndpointRemoteAddress + 1x udpEndpointRemotePort +
+   * 1x udpEndpointInstance = 39
+   */
+  u32_t  result_temp[39];
+
+  LWIP_UNUSED_ARG(value_len);
+
+  /* init struct to search next oid */
+  snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(result_temp));
+
+  /* iterate over all possible OIDs to find the next one */
+  pcb = udp_pcbs;
+  while (pcb != NULL) {
+    u32_t test_oid[LWIP_ARRAYSIZE(result_temp)];
+    u8_t idx = 0;
+
+    /* udpEndpointLocalAddressType + udpEndpointLocalAddress + udpEndpointLocalPort */
+    idx += snmp_ip_port_to_oid(&pcb->local_ip, pcb->local_port, &test_oid[idx]);
+
+    /* udpEndpointRemoteAddressType + udpEndpointRemoteAddress + udpEndpointRemotePort */
+    idx += snmp_ip_port_to_oid(&pcb->remote_ip, pcb->remote_port, &test_oid[idx]);
+
+    test_oid[idx] = 0; /* udpEndpointInstance */    
+    idx++;
+    
+    /* check generated OID: is it a candidate for the next one? */
+    snmp_next_oid_check(&state, test_oid, idx, NULL);
+    
+    pcb = pcb->next;
+  }
+
+  /* did we find a next one? */
+  if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
+    snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
+    /* fill in object properties */
+    return udp_endpointTable_get_cell_value_core(column, value);
+  } else {
+    /* not found */
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+}
+
+/* --- udpTable --- */
+
+#if LWIP_IPV4
+
+/* list of allowed value ranges for incoming OID */
+static const struct snmp_oid_range udp_Table_oid_ranges[] = {
+  { 0, 0xff   }, /* IP A        */
+  { 0, 0xff   }, /* IP B        */
+  { 0, 0xff   }, /* IP C        */
+  { 0, 0xff   }, /* IP D        */
+  { 1, 0xffff }  /* Port        */
+};
+
+static snmp_err_t 
+udp_Table_get_cell_value_core(struct udp_pcb *pcb, const u32_t* column, union snmp_variant_value* value, u32_t* value_len)
+{
+  LWIP_UNUSED_ARG(value_len);
+
+  switch (*column) {
+  case 1: /* udpLocalAddress */
+    /* set reference to PCB local IP and return a generic node that copies IP4 addresses */
+    value->u32 = ip_2_ip4(&pcb->local_ip)->addr;
+    break;
+  case 2: /* udpLocalPort */
+    /* set reference to PCB local port and return a generic node that copies u16_t values */
+    value->u32 = pcb->local_port;
+    break;
+  default:
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  return SNMP_ERR_NOERROR;
+}
+
+static snmp_err_t 
+udp_Table_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len)
+{
+  ip4_addr_t ip;
+  u16_t port;
+  struct udp_pcb *pcb;
+
+  /* check if incoming OID length and if values are in plausible range */
+  if (!snmp_oid_in_range(row_oid, row_oid_len, udp_Table_oid_ranges, LWIP_ARRAYSIZE(udp_Table_oid_ranges))) {
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  /* get IP and port from incoming OID */
+  snmp_oid_to_ip4(&row_oid[0], &ip); /* we know it succeeds because of oid_in_range check above */
+  port = (u16_t)row_oid[4];
+
+  /* find udp_pcb with requested ip and port*/
+  pcb = udp_pcbs;
+  while (pcb != NULL) {
+    if (IP_IS_V4_VAL(pcb->local_ip)) {
+      if (ip4_addr_cmp(&ip, ip_2_ip4(&pcb->local_ip)) && (port == pcb->local_port)) {
+        /* fill in object properties */
+        return udp_Table_get_cell_value_core(pcb, column, value, value_len);
+      }
+    }
+    pcb = pcb->next;
+  }
+
+  /* not found */
+  return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+static snmp_err_t 
+udp_Table_get_next_cell_instance_and_value(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len)
+{
+  struct udp_pcb *pcb;
+  struct snmp_next_oid_state state;
+  u32_t  result_temp[LWIP_ARRAYSIZE(udp_Table_oid_ranges)];
+
+  /* init struct to search next oid */
+  snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(udp_Table_oid_ranges));
+
+  /* iterate over all possible OIDs to find the next one */
+  pcb = udp_pcbs;
+  while (pcb != NULL) {
+    u32_t test_oid[LWIP_ARRAYSIZE(udp_Table_oid_ranges)];
+
+    if (IP_IS_V4_VAL(pcb->local_ip)) {
+      snmp_ip4_to_oid(ip_2_ip4(&pcb->local_ip), &test_oid[0]);
+      test_oid[4] = pcb->local_port;
+
+      /* check generated OID: is it a candidate for the next one? */
+      snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(udp_Table_oid_ranges), pcb);
+    }
+    
+    pcb = pcb->next;
+  }
+
+  /* did we find a next one? */
+  if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
+    snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
+    /* fill in object properties */
+    return udp_Table_get_cell_value_core((struct udp_pcb*)state.reference, column, value, value_len);
+  } else {
+    /* not found */
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+}
+
+#endif /* LWIP_IPV4 */
+
+static const struct snmp_scalar_node udp_inDatagrams    = SNMP_SCALAR_CREATE_NODE_READONLY(1, SNMP_ASN1_TYPE_COUNTER,   udp_get_value);
+static const struct snmp_scalar_node udp_noPorts        = SNMP_SCALAR_CREATE_NODE_READONLY(2, SNMP_ASN1_TYPE_COUNTER,   udp_get_value);
+static const struct snmp_scalar_node udp_inErrors       = SNMP_SCALAR_CREATE_NODE_READONLY(3, SNMP_ASN1_TYPE_COUNTER,   udp_get_value);
+static const struct snmp_scalar_node udp_outDatagrams   = SNMP_SCALAR_CREATE_NODE_READONLY(4, SNMP_ASN1_TYPE_COUNTER,   udp_get_value);
+static const struct snmp_scalar_node udp_HCInDatagrams  = SNMP_SCALAR_CREATE_NODE_READONLY(8, SNMP_ASN1_TYPE_COUNTER64, udp_get_value);
+static const struct snmp_scalar_node udp_HCOutDatagrams = SNMP_SCALAR_CREATE_NODE_READONLY(9, SNMP_ASN1_TYPE_COUNTER64, udp_get_value);
+
+#if LWIP_IPV4
+static const struct snmp_table_simple_col_def udp_Table_columns[] = {
+  { 1, SNMP_ASN1_TYPE_IPADDR,  SNMP_VARIANT_VALUE_TYPE_U32 }, /* udpLocalAddress */
+  { 2, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }  /* udpLocalPort */
+};
+static const struct snmp_table_simple_node udp_Table = SNMP_TABLE_CREATE_SIMPLE(5, udp_Table_columns, udp_Table_get_cell_value, udp_Table_get_next_cell_instance_and_value);
+#endif /* LWIP_IPV4 */
+
+static const struct snmp_table_simple_col_def udp_endpointTable_columns[] = {
+  /* all items except udpEndpointProcess are declared as not-accessible */   
+  { 8, SNMP_ASN1_TYPE_UNSIGNED32, SNMP_VARIANT_VALUE_TYPE_U32 }  /* udpEndpointProcess */
+};
+
+static const struct snmp_table_simple_node udp_endpointTable = SNMP_TABLE_CREATE_SIMPLE(7, udp_endpointTable_columns, udp_endpointTable_get_cell_value, udp_endpointTable_get_next_cell_instance_and_value);
+
+/* the following nodes access variables in LWIP stack from SNMP worker thread and must therefore be synced to LWIP (TCPIP) thread */ 
+CREATE_LWIP_SYNC_NODE(1, udp_inDatagrams)
+CREATE_LWIP_SYNC_NODE(2, udp_noPorts)
+CREATE_LWIP_SYNC_NODE(3, udp_inErrors)
+CREATE_LWIP_SYNC_NODE(4, udp_outDatagrams)
+#if LWIP_IPV4
+CREATE_LWIP_SYNC_NODE(5, udp_Table)
+#endif /* LWIP_IPV4 */
+CREATE_LWIP_SYNC_NODE(7, udp_endpointTable)
+CREATE_LWIP_SYNC_NODE(8, udp_HCInDatagrams)
+CREATE_LWIP_SYNC_NODE(9, udp_HCOutDatagrams)
+
+static const struct snmp_node* const udp_nodes[] = {
+  &SYNC_NODE_NAME(udp_inDatagrams).node.node,
+  &SYNC_NODE_NAME(udp_noPorts).node.node,
+  &SYNC_NODE_NAME(udp_inErrors).node.node,
+  &SYNC_NODE_NAME(udp_outDatagrams).node.node,
+#if LWIP_IPV4
+  &SYNC_NODE_NAME(udp_Table).node.node,
+#endif /* LWIP_IPV4 */
+  &SYNC_NODE_NAME(udp_endpointTable).node.node,
+  &SYNC_NODE_NAME(udp_HCInDatagrams).node.node,
+  &SYNC_NODE_NAME(udp_HCOutDatagrams).node.node
+};
+
+const struct snmp_tree_node snmp_mib2_udp_root = SNMP_CREATE_TREE_NODE(7, udp_nodes);
+#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_UDP */

+ 1668 - 1955
libs/thirdparty/lwip_2.1.2/src/apps/snmp/snmp_msg.c → libs/thirdparty/LwIP/src/apps/snmp/snmp_msg.c

@@ -1,1955 +1,1668 @@
-/**
- * @file
- * SNMP message processing (RFC1157).
- */
-
-/*
- * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
- * Copyright (c) 2016 Elias Oenal.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * Author: Christiaan Simons <christiaan.simons@axon.tv>
- *         Martin Hentschel <info@cl-soft.de>
- *         Elias Oenal <lwip@eliasoenal.com>
- */
-
-#include "lwip/apps/snmp_opts.h"
-
-#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
-
-#include "snmp_msg.h"
-#include "snmp_asn1.h"
-#include "snmp_core_priv.h"
-#include "lwip/ip_addr.h"
-#include "lwip/stats.h"
-
-#if LWIP_SNMP_V3
-#include "lwip/apps/snmpv3.h"
-#include "snmpv3_priv.h"
-#ifdef LWIP_HOOK_FILENAME
-#include LWIP_HOOK_FILENAME
-#endif
-#endif
-
-#include <string.h>
-
-#define SNMP_V3_AUTH_FLAG      0x01
-#define SNMP_V3_PRIV_FLAG      0x02
-
-/* Security levels */
-#define SNMP_V3_NOAUTHNOPRIV   0x00
-#define SNMP_V3_AUTHNOPRIV     SNMP_V3_AUTH_FLAG
-#define SNMP_V3_AUTHPRIV       (SNMP_V3_AUTH_FLAG | SNMP_V3_PRIV_FLAG)
-
-/* public (non-static) constants */
-/** SNMP community string */
-const char *snmp_community = SNMP_COMMUNITY;
-/** SNMP community string for write access */
-const char *snmp_community_write = SNMP_COMMUNITY_WRITE;
-/** SNMP community string for sending traps */
-const char *snmp_community_trap = SNMP_COMMUNITY_TRAP;
-
-snmp_write_callback_fct snmp_write_callback     = NULL;
-void                   *snmp_write_callback_arg = NULL;
-
-#if LWIP_SNMP_CONFIGURE_VERSIONS
-
-static u8_t v1_enabled = 1;
-static u8_t v2c_enabled = 1;
-static u8_t v3_enabled = 1;
-
-static u8_t
-snmp_version_enabled(u8_t version)
-{
-  if (version == SNMP_VERSION_1) {
-    return v1_enabled;
-  } else if (version == SNMP_VERSION_2c) {
-    return v2c_enabled;
-  }
-#if LWIP_SNMP_V3
-  else if (version == SNMP_VERSION_3) {
-    return v3_enabled;
-  }
-#endif
-  else {
-    LWIP_ASSERT("Invalid SNMP version", 0);
-    return 0;
-  }
-}
-
-u8_t
-snmp_v1_enabled(void)
-{
-  return snmp_version_enabled(SNMP_VERSION_1);
-}
-
-u8_t
-snmp_v2c_enabled(void)
-{
-  return snmp_version_enabled(SNMP_VERSION_2c);
-}
-
-u8_t
-snmp_v3_enabled(void)
-{
-  return snmp_version_enabled(SNMP_VERSION_3);
-}
-
-static void
-snmp_version_enable(u8_t version, u8_t enable)
-{
-  if (version == SNMP_VERSION_1) {
-    v1_enabled = enable;
-  } else if (version == SNMP_VERSION_2c) {
-    v2c_enabled = enable;
-  }
-#if LWIP_SNMP_V3
-  else if (version == SNMP_VERSION_3) {
-    v3_enabled = enable;
-  }
-#endif
-  else {
-    LWIP_ASSERT("Invalid SNMP version", 0);
-  }
-}
-
-void
-snmp_v1_enable(u8_t enable)
-{
-  snmp_version_enable(SNMP_VERSION_1, enable);
-}
-
-void
-snmp_v2c_enable(u8_t enable)
-{
-  snmp_version_enable(SNMP_VERSION_2c, enable);
-}
-
-void
-snmp_v3_enable(u8_t enable)
-{
-  snmp_version_enable(SNMP_VERSION_3, enable);
-}
-
-#endif
-
-/**
- * @ingroup snmp_core
- * Returns current SNMP community string.
- * @return current SNMP community string
- */
-const char *
-snmp_get_community(void)
-{
-  return snmp_community;
-}
-
-/**
- * @ingroup snmp_core
- * Sets SNMP community string.
- * The string itself (its storage) must be valid throughout the whole life of
- * program (or until it is changed to sth else).
- *
- * @param community is a pointer to new community string
- */
-void
-snmp_set_community(const char *const community)
-{
-  LWIP_ASSERT_CORE_LOCKED();
-  LWIP_ASSERT("community string is too long!", strlen(community) <= SNMP_MAX_COMMUNITY_STR_LEN);
-  snmp_community = community;
-}
-
-/**
- * @ingroup snmp_core
- * Returns current SNMP write-access community string.
- * @return current SNMP write-access community string
- */
-const char *
-snmp_get_community_write(void)
-{
-  return snmp_community_write;
-}
-
-/**
- * @ingroup snmp_traps
- * Returns current SNMP community string used for sending traps.
- * @return current SNMP community string used for sending traps
- */
-const char *
-snmp_get_community_trap(void)
-{
-  return snmp_community_trap;
-}
-
-/**
- * @ingroup snmp_core
- * Sets SNMP community string for write-access.
- * The string itself (its storage) must be valid throughout the whole life of
- * program (or until it is changed to sth else).
- *
- * @param community is a pointer to new write-access community string
- */
-void
-snmp_set_community_write(const char *const community)
-{
-  LWIP_ASSERT_CORE_LOCKED();
-  LWIP_ASSERT("community string must not be NULL", community != NULL);
-  LWIP_ASSERT("community string is too long!", strlen(community) <= SNMP_MAX_COMMUNITY_STR_LEN);
-  snmp_community_write = community;
-}
-
-/**
- * @ingroup snmp_traps
- * Sets SNMP community string used for sending traps.
- * The string itself (its storage) must be valid throughout the whole life of
- * program (or until it is changed to sth else).
- *
- * @param community is a pointer to new trap community string
- */
-void
-snmp_set_community_trap(const char *const community)
-{
-  LWIP_ASSERT_CORE_LOCKED();
-  LWIP_ASSERT("community string is too long!", strlen(community) <= SNMP_MAX_COMMUNITY_STR_LEN);
-  snmp_community_trap = community;
-}
-
-/**
- * @ingroup snmp_core
- * Callback fired on every successful write access
- */
-void
-snmp_set_write_callback(snmp_write_callback_fct write_callback, void *callback_arg)
-{
-  LWIP_ASSERT_CORE_LOCKED();
-  snmp_write_callback     = write_callback;
-  snmp_write_callback_arg = callback_arg;
-}
-
-/* ----------------------------------------------------------------------- */
-/* forward declarations */
-/* ----------------------------------------------------------------------- */
-
-static err_t snmp_process_get_request(struct snmp_request *request);
-static err_t snmp_process_getnext_request(struct snmp_request *request);
-static err_t snmp_process_getbulk_request(struct snmp_request *request);
-static err_t snmp_process_set_request(struct snmp_request *request);
-
-static err_t snmp_parse_inbound_frame(struct snmp_request *request);
-static err_t snmp_prepare_outbound_frame(struct snmp_request *request);
-static err_t snmp_complete_outbound_frame(struct snmp_request *request);
-static void snmp_execute_write_callbacks(struct snmp_request *request);
-
-
-/* ----------------------------------------------------------------------- */
-/* implementation */
-/* ----------------------------------------------------------------------- */
-
-void
-snmp_receive(void *handle, struct pbuf *p, const ip_addr_t *source_ip, u16_t port)
-{
-  err_t err;
-  struct snmp_request request;
-
-  memset(&request, 0, sizeof(request));
-  request.handle       = handle;
-  request.source_ip    = source_ip;
-  request.source_port  = port;
-  request.inbound_pbuf = p;
-
-  snmp_stats.inpkts++;
-
-  err = snmp_parse_inbound_frame(&request);
-  if (err == ERR_OK) {
-    err = snmp_prepare_outbound_frame(&request);
-    if (err == ERR_OK) {
-
-      if (request.error_status == SNMP_ERR_NOERROR) {
-        /* only process frame if we do not already have an error to return (e.g. all readonly) */
-        if (request.request_type == SNMP_ASN1_CONTEXT_PDU_GET_REQ) {
-          err = snmp_process_get_request(&request);
-        } else if (request.request_type == SNMP_ASN1_CONTEXT_PDU_GET_NEXT_REQ) {
-          err = snmp_process_getnext_request(&request);
-        } else if (request.request_type == SNMP_ASN1_CONTEXT_PDU_GET_BULK_REQ) {
-          err = snmp_process_getbulk_request(&request);
-        } else if (request.request_type == SNMP_ASN1_CONTEXT_PDU_SET_REQ) {
-          err = snmp_process_set_request(&request);
-        }
-      }
-#if LWIP_SNMP_V3
-      else {
-        struct snmp_varbind vb;
-
-        vb.next = NULL;
-        vb.prev = NULL;
-        vb.type = SNMP_ASN1_TYPE_COUNTER32;
-        vb.value_len = sizeof(u32_t);
-
-        switch (request.error_status) {
-          case SNMP_ERR_AUTHORIZATIONERROR: {
-            static const u32_t oid[] = { 1, 3, 6, 1, 6, 3, 15, 1, 1, 5, 0 };
-            snmp_oid_assign(&vb.oid, oid, LWIP_ARRAYSIZE(oid));
-            vb.value = &snmp_stats.wrongdigests;
-          }
-          break;
-          case SNMP_ERR_UNKNOWN_ENGINEID: {
-            static const u32_t oid[] = { 1, 3, 6, 1, 6, 3, 15, 1, 1, 4, 0 };
-            snmp_oid_assign(&vb.oid, oid, LWIP_ARRAYSIZE(oid));
-            vb.value = &snmp_stats.unknownengineids;
-          }
-          break;
-          case SNMP_ERR_UNKNOWN_SECURITYNAME: {
-            static const u32_t oid[] = { 1, 3, 6, 1, 6, 3, 15, 1, 1, 3, 0 };
-            snmp_oid_assign(&vb.oid, oid, LWIP_ARRAYSIZE(oid));
-            vb.value = &snmp_stats.unknownusernames;
-          }
-          break;
-          case SNMP_ERR_UNSUPPORTED_SECLEVEL: {
-            static const u32_t oid[] = { 1, 3, 6, 1, 6, 3, 15, 1, 1, 1, 0 };
-            snmp_oid_assign(&vb.oid, oid, LWIP_ARRAYSIZE(oid));
-            vb.value = &snmp_stats.unsupportedseclevels;
-          }
-          break;
-          case SNMP_ERR_NOTINTIMEWINDOW: {
-            static const u32_t oid[] = { 1, 3, 6, 1, 6, 3, 15, 1, 1, 2, 0 };
-            snmp_oid_assign(&vb.oid, oid, LWIP_ARRAYSIZE(oid));
-            vb.value = &snmp_stats.notintimewindows;
-          }
-          break;
-          case SNMP_ERR_DECRYIPTION_ERROR: {
-            static const u32_t oid[] = { 1, 3, 6, 1, 6, 3, 15, 1, 1, 6, 0 };
-            snmp_oid_assign(&vb.oid, oid, LWIP_ARRAYSIZE(oid));
-            vb.value = &snmp_stats.decryptionerrors;
-          }
-          break;
-          default:
-            /* Unknown or unhandled error_status */
-            err = ERR_ARG;
-        }
-
-        if (err == ERR_OK) {
-          snmp_append_outbound_varbind(&(request.outbound_pbuf_stream), &vb);
-          request.error_status = SNMP_ERR_NOERROR;
-        }
-
-        request.request_out_type = (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_REPORT);
-        request.request_id = request.msg_id;
-      }
-#endif
-
-      if (err == ERR_OK) {
-        err = snmp_complete_outbound_frame(&request);
-        printf("[%s][%u] err = %d\n", __FUNCTION__, __LINE__, err);
-      }
-
-        if (err == ERR_OK) {
-          printf("[%s][%u] ip: 0x%08x, port: %u\n", __FUNCTION__, __LINE__, request.source_ip->addr, request.source_port);
-          err = snmp_sendto(request.handle, request.outbound_pbuf, request.source_ip, request.source_port);
-          printf("[%s][%u] err = %d\n", __FUNCTION__, __LINE__, err);
-          printf("[%s][%u] request.request_type = %u\n", __FUNCTION__, __LINE__, request.request_type);
-          if ((request.request_type == SNMP_ASN1_CONTEXT_PDU_SET_REQ)
-              && (request.error_status == SNMP_ERR_NOERROR)
-              && (snmp_write_callback != NULL)) {
-            /* raise write notification for all written objects */
-            snmp_execute_write_callbacks(&request);
-          }
-        }
-      //}
-    }
-
-    if (request.outbound_pbuf != NULL) {
-      pbuf_free(request.outbound_pbuf);
-    }
-  }
-}
-
-static u8_t
-snmp_msg_getnext_validate_node_inst(struct snmp_node_instance *node_instance, void *validate_arg)
-{
-  if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_READ) != SNMP_NODE_INSTANCE_ACCESS_READ) || (node_instance->get_value == NULL)) {
-    return SNMP_ERR_NOSUCHINSTANCE;
-  }
-
-#if LWIP_HAVE_INT64
-  if ((node_instance->asn1_type == SNMP_ASN1_TYPE_COUNTER64) && (((struct snmp_request *)validate_arg)->version == SNMP_VERSION_1)) {
-    /* according to RFC 2089 skip Counter64 objects in GetNext requests from v1 clients */
-    return SNMP_ERR_NOSUCHINSTANCE;
-  }
-#endif
-
-  return SNMP_ERR_NOERROR;
-}
-
-static void
-snmp_process_varbind(struct snmp_request *request, struct snmp_varbind *vb, u8_t get_next)
-{
-  err_t err;
-  struct snmp_node_instance node_instance;
-  memset(&node_instance, 0, sizeof(node_instance));
-
-  if (get_next) {
-    struct snmp_obj_id result_oid;
-    request->error_status = snmp_get_next_node_instance_from_oid(vb->oid.id, vb->oid.len, snmp_msg_getnext_validate_node_inst, request,  &result_oid, &node_instance);
-
-    if (request->error_status == SNMP_ERR_NOERROR) {
-      snmp_oid_assign(&vb->oid, result_oid.id, result_oid.len);
-    }
-  } else {
-    request->error_status = snmp_get_node_instance_from_oid(vb->oid.id, vb->oid.len, &node_instance);
-
-    if (request->error_status == SNMP_ERR_NOERROR) {
-      /* use 'getnext_validate' method for validation to avoid code duplication (some checks have to be executed here) */
-      request->error_status = snmp_msg_getnext_validate_node_inst(&node_instance, request);
-
-      if (request->error_status != SNMP_ERR_NOERROR) {
-        if (node_instance.release_instance != NULL) {
-          node_instance.release_instance(&node_instance);
-        }
-      }
-    }
-  }
-
-  if (request->error_status != SNMP_ERR_NOERROR)  {
-    if (request->error_status >= SNMP_VARBIND_EXCEPTION_OFFSET) {
-      if ((request->version == SNMP_VERSION_2c) || request->version == SNMP_VERSION_3) {
-        /* in SNMP v2c a varbind related exception is stored in varbind and not in frame header */
-        vb->type = (SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_CLASS_CONTEXT | (request->error_status & SNMP_VARBIND_EXCEPTION_MASK));
-        vb->value_len = 0;
-
-        err = snmp_append_outbound_varbind(&(request->outbound_pbuf_stream), vb);
-        if (err == ERR_OK) {
-          /* we stored the exception in varbind -> go on */
-          request->error_status = SNMP_ERR_NOERROR;
-        } else if (err == ERR_BUF) {
-          request->error_status = SNMP_ERR_TOOBIG;
-        } else {
-          request->error_status = SNMP_ERR_GENERROR;
-        }
-      }
-    } else {
-      /* according to RFC 1157/1905, all other errors only return genError */
-      request->error_status = SNMP_ERR_GENERROR;
-    }
-  } else {
-    s16_t len = node_instance.get_value(&node_instance, vb->value);
-
-    if (len >= 0) {
-      vb->value_len = (u16_t)len; /* cast is OK because we checked >= 0 above */
-      vb->type = node_instance.asn1_type;
-
-      LWIP_ASSERT("SNMP_MAX_VALUE_SIZE is configured too low", (vb->value_len & ~SNMP_GET_VALUE_RAW_DATA) <= SNMP_MAX_VALUE_SIZE);
-      err = snmp_append_outbound_varbind(&request->outbound_pbuf_stream, vb);
-
-      if (err == ERR_BUF) {
-        request->error_status = SNMP_ERR_TOOBIG;
-      } else if (err != ERR_OK) {
-        request->error_status = SNMP_ERR_GENERROR;
-      }
-    } else {
-      request->error_status = SNMP_ERR_GENERROR;
-    }
-
-    if (node_instance.release_instance != NULL) {
-      node_instance.release_instance(&node_instance);
-    }
-  }
-}
-
-
-/**
- * Service an internal or external event for SNMP GET.
- *
- * @param request points to the associated message process state
- */
-static err_t
-snmp_process_get_request(struct snmp_request *request)
-{
-  snmp_vb_enumerator_err_t err;
-  struct snmp_varbind vb;
-  vb.value = request->value_buffer;
-
-  LWIP_DEBUGF(SNMP_DEBUG, ("SNMP get request\n"));
-
-  while (request->error_status == SNMP_ERR_NOERROR) {
-    err = snmp_vb_enumerator_get_next(&request->inbound_varbind_enumerator, &vb);
-    if (err == SNMP_VB_ENUMERATOR_ERR_OK) {
-      if ((vb.type == SNMP_ASN1_TYPE_NULL) && (vb.value_len == 0)) {
-        snmp_process_varbind(request, &vb, 0);
-      } else {
-        request->error_status = SNMP_ERR_GENERROR;
-      }
-    } else if (err == SNMP_VB_ENUMERATOR_ERR_EOVB) {
-      /* no more varbinds in request */
-      break;
-    } else if (err == SNMP_VB_ENUMERATOR_ERR_ASN1ERROR) {
-      /* malformed ASN.1, don't answer */
-      return ERR_ARG;
-    } else {
-      request->error_status = SNMP_ERR_GENERROR;
-    }
-  }
-
-  return ERR_OK;
-}
-
-/**
- * Service an internal or external event for SNMP GET.
- *
- * @param request points to the associated message process state
- */
-static err_t
-snmp_process_getnext_request(struct snmp_request *request)
-{
-  snmp_vb_enumerator_err_t err;
-  struct snmp_varbind vb;
-  vb.value = request->value_buffer;
-
-  LWIP_DEBUGF(SNMP_DEBUG, ("SNMP get-next request\n"));
-
-  while (request->error_status == SNMP_ERR_NOERROR) {
-    err = snmp_vb_enumerator_get_next(&request->inbound_varbind_enumerator, &vb);
-    if (err == SNMP_VB_ENUMERATOR_ERR_OK) {
-      if ((vb.type == SNMP_ASN1_TYPE_NULL) && (vb.value_len == 0)) {
-        snmp_process_varbind(request, &vb, 1);
-      } else {
-        request->error_status = SNMP_ERR_GENERROR;
-      }
-    } else if (err == SNMP_VB_ENUMERATOR_ERR_EOVB) {
-      /* no more varbinds in request */
-      break;
-    } else if (err == SNMP_VB_ENUMERATOR_ERR_ASN1ERROR) {
-      /* malformed ASN.1, don't answer */
-      return ERR_ARG;
-    } else {
-      request->error_status = SNMP_ERR_GENERROR;
-    }
-  }
-
-  return ERR_OK;
-}
-
-/**
- * Service an internal or external event for SNMP GETBULKT.
- *
- * @param request points to the associated message process state
- */
-static err_t
-snmp_process_getbulk_request(struct snmp_request *request)
-{
-  snmp_vb_enumerator_err_t err;
-  s32_t non_repeaters     = request->non_repeaters;
-  s32_t repetitions;
-  u16_t repetition_offset = 0;
-  struct snmp_varbind_enumerator repetition_varbind_enumerator;
-  struct snmp_varbind vb;
-  vb.value = request->value_buffer;
-
-  if (SNMP_LWIP_GETBULK_MAX_REPETITIONS > 0) {
-    repetitions = LWIP_MIN(request->max_repetitions, SNMP_LWIP_GETBULK_MAX_REPETITIONS);
-  } else {
-    repetitions = request->max_repetitions;
-  }
-
-  LWIP_DEBUGF(SNMP_DEBUG, ("SNMP get-bulk request\n"));
-
-  /* process non repeaters and first repetition */
-  while (request->error_status == SNMP_ERR_NOERROR) {
-    if (non_repeaters == 0) {
-      repetition_offset = request->outbound_pbuf_stream.offset;
-
-      if (repetitions == 0) {
-        /* do not resolve repeaters when repetitions is set to 0 */
-        break;
-      }
-      repetitions--;
-    }
-
-    err = snmp_vb_enumerator_get_next(&request->inbound_varbind_enumerator, &vb);
-    if (err == SNMP_VB_ENUMERATOR_ERR_EOVB) {
-      /* no more varbinds in request */
-      break;
-    } else if (err == SNMP_VB_ENUMERATOR_ERR_ASN1ERROR) {
-      /* malformed ASN.1, don't answer */
-      return ERR_ARG;
-    } else if ((err != SNMP_VB_ENUMERATOR_ERR_OK) || (vb.type != SNMP_ASN1_TYPE_NULL) || (vb.value_len != 0)) {
-      request->error_status = SNMP_ERR_GENERROR;
-    } else {
-      snmp_process_varbind(request, &vb, 1);
-      non_repeaters--;
-    }
-  }
-
-  /* process repetitions > 1 */
-  while ((request->error_status == SNMP_ERR_NOERROR) && (repetitions > 0) && (request->outbound_pbuf_stream.offset != repetition_offset)) {
-
-    u8_t all_endofmibview = 1;
-
-    snmp_vb_enumerator_init(&repetition_varbind_enumerator, request->outbound_pbuf, repetition_offset, request->outbound_pbuf_stream.offset - repetition_offset);
-    repetition_offset = request->outbound_pbuf_stream.offset; /* for next loop */
-
-    while (request->error_status == SNMP_ERR_NOERROR) {
-      vb.value = NULL; /* do NOT decode value (we enumerate outbound buffer here, so all varbinds have values assigned) */
-      err = snmp_vb_enumerator_get_next(&repetition_varbind_enumerator, &vb);
-      if (err == SNMP_VB_ENUMERATOR_ERR_OK) {
-        vb.value = request->value_buffer;
-        snmp_process_varbind(request, &vb, 1);
-
-        if (request->error_status != SNMP_ERR_NOERROR) {
-          /* already set correct error-index (here it cannot be taken from inbound varbind enumerator) */
-          request->error_index = request->non_repeaters + repetition_varbind_enumerator.varbind_count;
-        } else if (vb.type != (SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTEXT_VARBIND_END_OF_MIB_VIEW)) {
-          all_endofmibview = 0;
-        }
-      } else if (err == SNMP_VB_ENUMERATOR_ERR_EOVB) {
-        /* no more varbinds in request */
-        break;
-      } else {
-        LWIP_DEBUGF(SNMP_DEBUG, ("Very strange, we cannot parse the varbind output that we created just before!"));
-        request->error_status = SNMP_ERR_GENERROR;
-        request->error_index  = request->non_repeaters + repetition_varbind_enumerator.varbind_count;
-      }
-    }
-
-    if ((request->error_status == SNMP_ERR_NOERROR) && all_endofmibview) {
-      /* stop when all varbinds in a loop return EndOfMibView */
-      break;
-    }
-
-    repetitions--;
-  }
-
-  if (request->error_status == SNMP_ERR_TOOBIG) {
-    /* for GetBulk it is ok, if not all requested variables fit into the response -> just return the varbinds added so far */
-    request->error_status = SNMP_ERR_NOERROR;
-  }
-
-  return ERR_OK;
-}
-
-/**
- * Service an internal or external event for SNMP SET.
- *
- * @param request points to the associated message process state
- */
-static err_t
-snmp_process_set_request(struct snmp_request *request)
-{
-  snmp_vb_enumerator_err_t err;
-  struct snmp_varbind vb;
-  vb.value = request->value_buffer;
-
-  LWIP_DEBUGF(SNMP_DEBUG, ("SNMP set request\n"));
-
-  /* perform set test on all objects */
-  while (request->error_status == SNMP_ERR_NOERROR) {
-    err = snmp_vb_enumerator_get_next(&request->inbound_varbind_enumerator, &vb);
-    if (err == SNMP_VB_ENUMERATOR_ERR_OK) {
-      struct snmp_node_instance node_instance;
-      memset(&node_instance, 0, sizeof(node_instance));
-
-      request->error_status = snmp_get_node_instance_from_oid(vb.oid.id, vb.oid.len, &node_instance);
-      if (request->error_status == SNMP_ERR_NOERROR) {
-        if (node_instance.asn1_type != vb.type) {
-          request->error_status = SNMP_ERR_WRONGTYPE;
-        } else if (((node_instance.access & SNMP_NODE_INSTANCE_ACCESS_WRITE) != SNMP_NODE_INSTANCE_ACCESS_WRITE) || (node_instance.set_value == NULL)) {
-          request->error_status = SNMP_ERR_NOTWRITABLE;
-        } else {
-          if (node_instance.set_test != NULL) {
-            request->error_status = node_instance.set_test(&node_instance, vb.value_len, vb.value);
-          }
-        }
-
-        if (node_instance.release_instance != NULL) {
-          node_instance.release_instance(&node_instance);
-        }
-      }
-    } else if (err == SNMP_VB_ENUMERATOR_ERR_EOVB) {
-      /* no more varbinds in request */
-      break;
-    } else if (err == SNMP_VB_ENUMERATOR_ERR_INVALIDLENGTH) {
-      request->error_status = SNMP_ERR_WRONGLENGTH;
-    } else if (err == SNMP_VB_ENUMERATOR_ERR_ASN1ERROR) {
-      /* malformed ASN.1, don't answer */
-      return ERR_ARG;
-    } else {
-      request->error_status = SNMP_ERR_GENERROR;
-    }
-  }
-
-  /* perform real set operation on all objects */
-  if (request->error_status == SNMP_ERR_NOERROR) {
-    snmp_vb_enumerator_init(&request->inbound_varbind_enumerator, request->inbound_pbuf, request->inbound_varbind_offset, request->inbound_varbind_len);
-    while (request->error_status == SNMP_ERR_NOERROR) {
-      err = snmp_vb_enumerator_get_next(&request->inbound_varbind_enumerator, &vb);
-      if (err == SNMP_VB_ENUMERATOR_ERR_OK) {
-        struct snmp_node_instance node_instance;
-        memset(&node_instance, 0, sizeof(node_instance));
-        request->error_status = snmp_get_node_instance_from_oid(vb.oid.id, vb.oid.len, &node_instance);
-        if (request->error_status == SNMP_ERR_NOERROR) {
-          if (node_instance.set_value(&node_instance, vb.value_len, vb.value) != SNMP_ERR_NOERROR) {
-            if (request->inbound_varbind_enumerator.varbind_count == 1) {
-              request->error_status = SNMP_ERR_COMMITFAILED;
-            } else {
-              /* we cannot undo the set operations done so far */
-              request->error_status = SNMP_ERR_UNDOFAILED;
-            }
-          }
-
-          if (node_instance.release_instance != NULL) {
-            node_instance.release_instance(&node_instance);
-          }
-        }
-      } else if (err == SNMP_VB_ENUMERATOR_ERR_EOVB) {
-        /* no more varbinds in request */
-        break;
-      } else {
-        /* first time enumerating varbinds work but second time not, although nothing should have changed in between ??? */
-        request->error_status = SNMP_ERR_GENERROR;
-      }
-    }
-  }
-
-  return ERR_OK;
-}
-
-#define PARSE_EXEC(code, retValue) \
-  if ((code) != ERR_OK) { \
-    LWIP_DEBUGF(SNMP_DEBUG, ("Malformed ASN.1 detected.\n")); \
-    snmp_stats.inasnparseerrs++; \
-    return retValue; \
-  }
-
-#define PARSE_ASSERT(cond, retValue) \
-  if (!(cond)) { \
-    LWIP_DEBUGF(SNMP_DEBUG, ("SNMP parse assertion failed!: " # cond)); \
-    snmp_stats.inasnparseerrs++; \
-    return retValue; \
-  }
-
-#define BUILD_EXEC(code, retValue) \
-  if ((code) != ERR_OK) { \
-    LWIP_DEBUGF(SNMP_DEBUG, ("SNMP error during creation of outbound frame!: " # code)); \
-    return retValue; \
-  }
-
-#define IF_PARSE_EXEC(code)   PARSE_EXEC(code, ERR_ARG)
-#define IF_PARSE_ASSERT(code) PARSE_ASSERT(code, ERR_ARG)
-
-/**
- * Checks and decodes incoming SNMP message header, logs header errors.
- *
- * @param request points to the current message request state return
- * @return
- * - ERR_OK SNMP header is sane and accepted
- * - ERR_VAL SNMP header is either malformed or rejected
- */
-static err_t
-snmp_parse_inbound_frame(struct snmp_request *request)
-{
-  struct snmp_pbuf_stream pbuf_stream;
-  struct snmp_asn1_tlv tlv;
-  s32_t parent_tlv_value_len;
-  s32_t s32_value;
-  err_t err;
-#if LWIP_SNMP_V3
-  snmpv3_auth_algo_t auth;
-  snmpv3_priv_algo_t priv;
-#endif
-
-  IF_PARSE_EXEC(snmp_pbuf_stream_init(&pbuf_stream, request->inbound_pbuf, 0, request->inbound_pbuf->tot_len));
-
-  /* decode main container consisting of version, community and PDU */
-  IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
-  IF_PARSE_ASSERT((tlv.type == SNMP_ASN1_TYPE_SEQUENCE) && (tlv.value_len == pbuf_stream.length));
-  parent_tlv_value_len = tlv.value_len;
-
-  /* decode version */
-  IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
-  IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER);
-  parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
-  IF_PARSE_ASSERT(parent_tlv_value_len > 0);
-
-  IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &s32_value));
-
-  if (((s32_value != SNMP_VERSION_1) &&
-       (s32_value != SNMP_VERSION_2c)
-#if LWIP_SNMP_V3
-       && (s32_value != SNMP_VERSION_3)
-#endif
-      )
-#if LWIP_SNMP_CONFIGURE_VERSIONS
-      || (!snmp_version_enabled(s32_value))
-#endif
-     ) {
-    /* unsupported SNMP version */
-    snmp_stats.inbadversions++;
-    return ERR_ARG;
-  }
-  request->version = (u8_t)s32_value;
-
-#if LWIP_SNMP_V3
-  if (request->version == SNMP_VERSION_3) {
-    u16_t u16_value;
-    u16_t inbound_msgAuthenticationParameters_offset;
-
-    /* SNMPv3 doesn't use communities */
-    /* @todo: Differentiate read/write access */
-    strncpy((char *)request->community, snmp_community, SNMP_MAX_COMMUNITY_STR_LEN);
-    request->community[SNMP_MAX_COMMUNITY_STR_LEN] = 0; /* ensure NULL termination (strncpy does NOT guarantee it!) */
-    request->community_strlen = (u16_t)strnlen((char *)request->community, SNMP_MAX_COMMUNITY_STR_LEN);
-
-    /* RFC3414 globalData */
-    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
-    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_SEQUENCE);
-    parent_tlv_value_len -= SNMP_ASN1_TLV_HDR_LENGTH(tlv);
-    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
-
-    /* decode msgID */
-    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
-    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER);
-    parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
-    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
-
-    IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &s32_value));
-    request->msg_id = s32_value;
-
-    /* decode msgMaxSize */
-    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
-    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER);
-    parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
-    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
-
-    IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &s32_value));
-    request->msg_max_size = s32_value;
-
-    /* decode msgFlags */
-    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
-    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
-    parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
-    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
-
-    IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &s32_value));
-    request->msg_flags = (u8_t)s32_value;
-
-    /* decode msgSecurityModel */
-    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
-    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER);
-    parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
-    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
-
-    IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &s32_value));
-    request->msg_security_model = s32_value;
-
-    /* RFC3414 msgSecurityParameters
-     * The User-based Security Model defines the contents of the OCTET
-     * STRING as a SEQUENCE.
-     *
-     * We skip the protective dummy OCTET STRING header
-     * to access the SEQUENCE header.
-     */
-    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
-    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
-    parent_tlv_value_len -= SNMP_ASN1_TLV_HDR_LENGTH(tlv);
-    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
-
-    /* msgSecurityParameters SEQUENCE header */
-    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
-    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_SEQUENCE);
-    parent_tlv_value_len -= SNMP_ASN1_TLV_HDR_LENGTH(tlv);
-    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
-
-    /* decode msgAuthoritativeEngineID */
-    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
-    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
-    parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
-    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
-
-    IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->msg_authoritative_engine_id,
-                                    &u16_value, SNMP_V3_MAX_ENGINE_ID_LENGTH));
-    request->msg_authoritative_engine_id_len = (u8_t)u16_value;
-
-    /* msgAuthoritativeEngineBoots */
-    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
-    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER);
-    parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
-    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
-    IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &request->msg_authoritative_engine_boots));
-
-    /* msgAuthoritativeEngineTime */
-    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
-    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER);
-    parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
-    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
-    IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &request->msg_authoritative_engine_time));
-
-    /* msgUserName */
-    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
-    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
-    parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
-    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
-
-    IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->msg_user_name,
-                                    &u16_value, SNMP_V3_MAX_USER_LENGTH));
-    request->msg_user_name_len = (u8_t)u16_value;
-
-    /* msgAuthenticationParameters */
-    memset(request->msg_authentication_parameters, 0, SNMP_V3_MAX_AUTH_PARAM_LENGTH);
-    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
-    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
-    parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
-    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
-    /* Remember position */
-    inbound_msgAuthenticationParameters_offset = pbuf_stream.offset;
-    LWIP_UNUSED_ARG(inbound_msgAuthenticationParameters_offset);
-    /* Read auth parameters */
-    /* IF_PARSE_ASSERT(tlv.value_len <= SNMP_V3_MAX_AUTH_PARAM_LENGTH); */
-    IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->msg_authentication_parameters,
-                                    &u16_value, tlv.value_len));
-    request->msg_authentication_parameters_len = (u8_t)u16_value;
-
-    /* msgPrivacyParameters */
-    memset(request->msg_privacy_parameters, 0, SNMP_V3_MAX_PRIV_PARAM_LENGTH);
-    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
-    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
-    parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
-    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
-
-    IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->msg_privacy_parameters,
-                                    &u16_value, SNMP_V3_MAX_PRIV_PARAM_LENGTH));
-    request->msg_privacy_parameters_len = (u8_t)u16_value;
-
-    /* validate securityParameters here (do this after decoding because we don't want to increase other counters for wrong frames)
-     * 1) securityParameters was correctly serialized if we reach here.
-     * 2) securityParameters are already cached.
-     * 3) if msgAuthoritativeEngineID is unknown, zero-length or too long:
-         b) https://tools.ietf.org/html/rfc3414#section-7
-     */
-    {
-      const char *eid;
-      u8_t eid_len;
-
-      snmpv3_get_engine_id(&eid, &eid_len);
-
-      if ((request->msg_authoritative_engine_id_len == 0) ||
-          (request->msg_authoritative_engine_id_len != eid_len) ||
-          (memcmp(eid, request->msg_authoritative_engine_id, eid_len) != 0)) {
-        snmp_stats.unknownengineids++;
-        request->msg_flags = 0; /* noauthnopriv */
-        request->error_status = SNMP_ERR_UNKNOWN_ENGINEID;
-        return ERR_OK;
-      }
-    }
-
-    /* 4) verify username */
-    if (snmpv3_get_user((char *)request->msg_user_name, &auth, NULL, &priv, NULL)) {
-      snmp_stats.unknownusernames++;
-      request->msg_flags = 0; /* noauthnopriv */
-      request->error_status = SNMP_ERR_UNKNOWN_SECURITYNAME;
-      return ERR_OK;
-    }
-
-    /* 5) verify security level */
-    switch (request->msg_flags & (SNMP_V3_AUTH_FLAG | SNMP_V3_PRIV_FLAG)) {
-      case SNMP_V3_NOAUTHNOPRIV:
-        if ((auth != SNMP_V3_AUTH_ALGO_INVAL) || (priv != SNMP_V3_PRIV_ALGO_INVAL)) {
-          /* Invalid security level for user */
-          snmp_stats.unsupportedseclevels++;
-          request->msg_flags = SNMP_V3_NOAUTHNOPRIV;
-          request->error_status = SNMP_ERR_UNSUPPORTED_SECLEVEL;
-          return ERR_OK;
-        }
-        break;
-#if LWIP_SNMP_V3_CRYPTO
-      case SNMP_V3_AUTHNOPRIV:
-        if ((auth == SNMP_V3_AUTH_ALGO_INVAL) || (priv != SNMP_V3_PRIV_ALGO_INVAL)) {
-          /* Invalid security level for user */
-          snmp_stats.unsupportedseclevels++;
-          request->msg_flags = SNMP_V3_NOAUTHNOPRIV;
-          request->error_status = SNMP_ERR_UNSUPPORTED_SECLEVEL;
-          return ERR_OK;
-        }
-        break;
-      case SNMP_V3_AUTHPRIV:
-        if ((auth == SNMP_V3_AUTH_ALGO_INVAL) || (priv == SNMP_V3_PRIV_ALGO_INVAL)) {
-          /* Invalid security level for user */
-          snmp_stats.unsupportedseclevels++;
-          request->msg_flags = SNMP_V3_NOAUTHNOPRIV;
-          request->error_status = SNMP_ERR_UNSUPPORTED_SECLEVEL;
-          return ERR_OK;
-        }
-        break;
-#endif
-      default:
-        snmp_stats.unsupportedseclevels++;
-        request->msg_flags = SNMP_V3_NOAUTHNOPRIV;
-        request->error_status = SNMP_ERR_UNSUPPORTED_SECLEVEL;
-        return ERR_OK;
-    }
-
-    /* 6) if securitylevel specifies authentication, authenticate message. */
-#if LWIP_SNMP_V3_CRYPTO
-    if (request->msg_flags & SNMP_V3_AUTH_FLAG) {
-      const u8_t zero_arr[SNMP_V3_MAX_AUTH_PARAM_LENGTH] = { 0 };
-      u8_t key[20];
-      u8_t hmac[LWIP_MAX(SNMP_V3_SHA_LEN, SNMP_V3_MD5_LEN)];
-      struct snmp_pbuf_stream auth_stream;
-
-      if (request->msg_authentication_parameters_len > SNMP_V3_MAX_AUTH_PARAM_LENGTH) {
-        snmp_stats.wrongdigests++;
-        request->msg_flags = SNMP_V3_NOAUTHNOPRIV;
-        request->error_status = SNMP_ERR_AUTHORIZATIONERROR;
-        return ERR_OK;
-      }
-
-      /* Rewind stream */
-      IF_PARSE_EXEC(snmp_pbuf_stream_init(&auth_stream, request->inbound_pbuf, 0, request->inbound_pbuf->tot_len));
-      IF_PARSE_EXEC(snmp_pbuf_stream_seek_abs(&auth_stream, inbound_msgAuthenticationParameters_offset));
-      /* Set auth parameters to zero for verification */
-      IF_PARSE_EXEC(snmp_asn1_enc_raw(&auth_stream, zero_arr, request->msg_authentication_parameters_len));
-
-      /* Verify authentication */
-      IF_PARSE_EXEC(snmp_pbuf_stream_init(&auth_stream, request->inbound_pbuf, 0, request->inbound_pbuf->tot_len));
-
-      IF_PARSE_EXEC(snmpv3_get_user((char *)request->msg_user_name, &auth, key, NULL, NULL));
-      IF_PARSE_EXEC(snmpv3_auth(&auth_stream, request->inbound_pbuf->tot_len, key, auth, hmac));
-
-      if (memcmp(request->msg_authentication_parameters, hmac, SNMP_V3_MAX_AUTH_PARAM_LENGTH)) {
-        snmp_stats.wrongdigests++;
-        request->msg_flags = SNMP_V3_NOAUTHNOPRIV;
-        request->error_status = SNMP_ERR_AUTHORIZATIONERROR;
-        return ERR_OK;
-      }
-
-      /* 7) if securitylevel specifies authentication, verify engineboots, enginetime and lastenginetime */
-      {
-        s32_t boots = snmpv3_get_engine_boots_internal();
-        if ((request->msg_authoritative_engine_boots != boots) || (boots == 2147483647UL)) {
-          snmp_stats.notintimewindows++;
-          request->msg_flags = SNMP_V3_AUTHNOPRIV;
-          request->error_status = SNMP_ERR_NOTINTIMEWINDOW;
-          return ERR_OK;
-        }
-      }
-      {
-        s32_t time = snmpv3_get_engine_time_internal();
-        if (request->msg_authoritative_engine_time > (time + 150)) {
-          snmp_stats.notintimewindows++;
-          request->msg_flags = SNMP_V3_AUTHNOPRIV;
-          request->error_status = SNMP_ERR_NOTINTIMEWINDOW;
-          return ERR_OK;
-        } else if (time > 150) {
-          if (request->msg_authoritative_engine_time < (time - 150)) {
-            snmp_stats.notintimewindows++;
-            request->msg_flags = SNMP_V3_AUTHNOPRIV;
-            request->error_status = SNMP_ERR_NOTINTIMEWINDOW;
-            return ERR_OK;
-          }
-        }
-      }
-    }
-#endif
-
-    /* 8) if securitylevel specifies privacy, decrypt message. */
-#if LWIP_SNMP_V3_CRYPTO
-    if (request->msg_flags & SNMP_V3_PRIV_FLAG) {
-      /* Decrypt message */
-
-      u8_t key[20];
-
-      IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
-      IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
-      parent_tlv_value_len -= SNMP_ASN1_TLV_HDR_LENGTH(tlv);
-      IF_PARSE_ASSERT(parent_tlv_value_len > 0);
-
-      IF_PARSE_EXEC(snmpv3_get_user((char *)request->msg_user_name, NULL, NULL, &priv, key));
-      if (snmpv3_crypt(&pbuf_stream, tlv.value_len, key,
-                       request->msg_privacy_parameters, request->msg_authoritative_engine_boots,
-                       request->msg_authoritative_engine_time, priv, SNMP_V3_PRIV_MODE_DECRYPT) != ERR_OK) {
-        snmp_stats.decryptionerrors++;
-        request->msg_flags = SNMP_V3_AUTHNOPRIV;
-        request->error_status = SNMP_ERR_DECRYIPTION_ERROR;
-        return ERR_OK;
-      }
-    }
-#endif
-    /* 9) calculate max size of scoped pdu?
-     * 10) securityname for user is retrieved from usertable?
-     * 11) security data is cached?
-     * 12)
-     */
-
-    /* Scoped PDU
-     * Encryption context
-     */
-    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
-    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_SEQUENCE);
-    parent_tlv_value_len -= SNMP_ASN1_TLV_HDR_LENGTH(tlv);
-    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
-
-    /* contextEngineID */
-    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
-    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
-    parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
-    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
-
-    IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->context_engine_id,
-                                    &u16_value, SNMP_V3_MAX_ENGINE_ID_LENGTH));
-    request->context_engine_id_len = (u8_t)u16_value;
-    /* TODO: do we need to verify this contextengineid too? */
-
-    /* contextName */
-    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
-    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
-    parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
-    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
-
-    IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->context_name,
-                                    &u16_value, SNMP_V3_MAX_ENGINE_ID_LENGTH));
-    request->context_name_len = (u8_t)u16_value;
-    /* TODO: do we need to verify this contextname too? */
-  } else
-#endif
-  {
-    /* decode community */
-    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
-    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
-    parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
-    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
-
-    err = snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->community, &request->community_strlen, SNMP_MAX_COMMUNITY_STR_LEN);
-    if (err == ERR_MEM) {
-      /* community string does not fit in our buffer -> its too long -> its invalid */
-      request->community_strlen = 0;
-      snmp_pbuf_stream_seek(&pbuf_stream, tlv.value_len);
-    } else {
-      IF_PARSE_ASSERT(err == ERR_OK);
-    }
-    /* add zero terminator */
-    request->community[request->community_strlen] = 0;
-  }
-
-  /* decode PDU type (next container level) */
-  IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
-  IF_PARSE_ASSERT(tlv.value_len <= pbuf_stream.length);
-  request->inbound_padding_len = pbuf_stream.length - tlv.value_len;
-  parent_tlv_value_len = tlv.value_len;
-
-  /* validate PDU type */
-  switch (tlv.type) {
-    case (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_GET_REQ):
-      /* GetRequest PDU */
-      snmp_stats.ingetrequests++;
-      break;
-    case (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_GET_NEXT_REQ):
-      /* GetNextRequest PDU */
-      snmp_stats.ingetnexts++;
-      break;
-    case (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_GET_BULK_REQ):
-      /* GetBulkRequest PDU */
-      if (request->version < SNMP_VERSION_2c) {
-        /* RFC2089: invalid, drop packet */
-        return ERR_ARG;
-      }
-      break;
-    case (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_SET_REQ):
-      /* SetRequest PDU */
-      snmp_stats.insetrequests++;
-      break;
-    default:
-      /* unsupported input PDU for this agent (no parse error) */
-      LWIP_DEBUGF(SNMP_DEBUG, ("Unknown/Invalid SNMP PDU type received: %d", tlv.type)); \
-      return ERR_ARG;
-  }
-  request->request_type = tlv.type & SNMP_ASN1_DATATYPE_MASK;
-  request->request_out_type = (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_GET_RESP);
-
-  /* validate community (do this after decoding PDU type because we don't want to increase 'inbadcommunitynames' for wrong frame types */
-  if (request->community_strlen == 0) {
-    /* community string was too long or really empty*/
-    snmp_stats.inbadcommunitynames++;
-    snmp_authfail_trap();
-    return ERR_ARG;
-  } else if (request->request_type == SNMP_ASN1_CONTEXT_PDU_SET_REQ) {
-    if (snmp_community_write[0] == 0) {
-      /* our write community is empty, that means all our objects are readonly */
-      request->error_status = SNMP_ERR_NOTWRITABLE;
-      request->error_index  = 1;
-    } else if (strncmp(snmp_community_write, (const char *)request->community, SNMP_MAX_COMMUNITY_STR_LEN) != 0) {
-      /* community name does not match */
-      snmp_stats.inbadcommunitynames++;
-      snmp_authfail_trap();
-      return ERR_ARG;
-    }
-  } else {
-    if (strncmp(snmp_community, (const char *)request->community, SNMP_MAX_COMMUNITY_STR_LEN) != 0) {
-      /* community name does not match */
-      snmp_stats.inbadcommunitynames++;
-      snmp_authfail_trap();
-      return ERR_ARG;
-    }
-  }
-
-  /* decode request ID */
-  IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
-  IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER);
-  parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
-  IF_PARSE_ASSERT(parent_tlv_value_len > 0);
-
-  IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &request->request_id));
-
-  /* decode error status / non-repeaters */
-  IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
-  IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER);
-  parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
-  IF_PARSE_ASSERT(parent_tlv_value_len > 0);
-
-  if (request->request_type == SNMP_ASN1_CONTEXT_PDU_GET_BULK_REQ) {
-    IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &request->non_repeaters));
-    if (request->non_repeaters < 0) {
-      /* RFC 1905, 4.2.3 */
-      request->non_repeaters = 0;
-    }
-  } else {
-    /* only check valid value, don't touch 'request->error_status', maybe a response error status was already set to above; */
-    IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &s32_value));
-    IF_PARSE_ASSERT(s32_value == SNMP_ERR_NOERROR);
-  }
-
-  /* decode error index / max-repetitions */
-  IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
-  IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER);
-  parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
-  IF_PARSE_ASSERT(parent_tlv_value_len > 0);
-
-  if (request->request_type == SNMP_ASN1_CONTEXT_PDU_GET_BULK_REQ) {
-    IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &request->max_repetitions));
-    if (request->max_repetitions < 0) {
-      /* RFC 1905, 4.2.3 */
-      request->max_repetitions = 0;
-    }
-  } else {
-    IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &request->error_index));
-    IF_PARSE_ASSERT(s32_value == 0);
-  }
-
-  /* decode varbind-list type (next container level) */
-  IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
-  IF_PARSE_ASSERT((tlv.type == SNMP_ASN1_TYPE_SEQUENCE) && (tlv.value_len <= pbuf_stream.length));
-
-  request->inbound_varbind_offset = pbuf_stream.offset;
-  request->inbound_varbind_len    = pbuf_stream.length - request->inbound_padding_len;
-  snmp_vb_enumerator_init(&(request->inbound_varbind_enumerator), request->inbound_pbuf, request->inbound_varbind_offset, request->inbound_varbind_len);
-
-  return ERR_OK;
-}
-
-#define OF_BUILD_EXEC(code) BUILD_EXEC(code, ERR_ARG)
-
-static err_t
-snmp_prepare_outbound_frame(struct snmp_request *request)
-{
-  struct snmp_asn1_tlv tlv;
-  struct snmp_pbuf_stream *pbuf_stream = &(request->outbound_pbuf_stream);
-
-  /* try allocating pbuf(s) for maximum response size */
-  request->outbound_pbuf = pbuf_alloc(PBUF_TRANSPORT, 1472, PBUF_RAM);
-  if (request->outbound_pbuf == NULL) {
-    return ERR_MEM;
-  }
-
-  snmp_pbuf_stream_init(pbuf_stream, request->outbound_pbuf, 0, request->outbound_pbuf->tot_len);
-
-  /* 'Message' sequence */
-  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 3, 0);
-  OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
-
-  /* version */
-  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
-  snmp_asn1_enc_s32t_cnt(request->version, &tlv.value_len);
-  OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
-  OF_BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, request->version) );
-
-#if LWIP_SNMP_V3
-  if (request->version < SNMP_VERSION_3) {
-#endif
-    /* community */
-    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, request->community_strlen);
-    OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
-    OF_BUILD_EXEC( snmp_asn1_enc_raw(pbuf_stream, request->community, request->community_strlen) );
-#if LWIP_SNMP_V3
-  } else {
-    const char *id;
-
-    /* globalData */
-    request->outbound_msg_global_data_offset = pbuf_stream->offset;
-    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 1, 0);
-    OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
-
-    /* msgID */
-    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 1);
-    snmp_asn1_enc_s32t_cnt(request->msg_id, &tlv.value_len);
-    OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
-    OF_BUILD_EXEC(snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, request->msg_id));
-
-    /* msgMaxSize */
-    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 1);
-    snmp_asn1_enc_s32t_cnt(request->msg_max_size, &tlv.value_len);
-    OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
-    OF_BUILD_EXEC(snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, request->msg_max_size));
-
-    /* msgFlags */
-    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, 1);
-    OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
-    OF_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, &request->msg_flags, 1));
-
-    /* msgSecurityModel */
-    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 1);
-    snmp_asn1_enc_s32t_cnt(request->msg_security_model, &tlv.value_len);
-    OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
-    OF_BUILD_EXEC(snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, request->msg_security_model));
-
-    /* end of msgGlobalData */
-    request->outbound_msg_global_data_end = pbuf_stream->offset;
-
-    /* msgSecurityParameters */
-    request->outbound_msg_security_parameters_str_offset = pbuf_stream->offset;
-    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 1, 0);
-    OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
-
-    request->outbound_msg_security_parameters_seq_offset = pbuf_stream->offset;
-    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 1, 0);
-    OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
-
-    /* msgAuthoritativeEngineID */
-    snmpv3_get_engine_id(&id, &request->msg_authoritative_engine_id_len);
-    MEMCPY(request->msg_authoritative_engine_id, id, request->msg_authoritative_engine_id_len);
-    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, request->msg_authoritative_engine_id_len);
-    OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
-    OF_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, request->msg_authoritative_engine_id, request->msg_authoritative_engine_id_len));
-
-    request->msg_authoritative_engine_time = snmpv3_get_engine_time();
-    request->msg_authoritative_engine_boots = snmpv3_get_engine_boots();
-
-    /* msgAuthoritativeEngineBoots */
-    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
-    snmp_asn1_enc_s32t_cnt(request->msg_authoritative_engine_boots, &tlv.value_len);
-    OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
-    OF_BUILD_EXEC(snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, request->msg_authoritative_engine_boots));
-
-    /* msgAuthoritativeEngineTime */
-    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
-    snmp_asn1_enc_s32t_cnt(request->msg_authoritative_engine_time, &tlv.value_len);
-    OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
-    OF_BUILD_EXEC(snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, request->msg_authoritative_engine_time));
-
-    /* msgUserName */
-    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, request->msg_user_name_len);
-    OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
-    OF_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, request->msg_user_name, request->msg_user_name_len));
-
-#if LWIP_SNMP_V3_CRYPTO
-    /* msgAuthenticationParameters */
-    if (request->msg_flags & SNMP_V3_AUTH_FLAG) {
-      memset(request->msg_authentication_parameters, 0, SNMP_V3_MAX_AUTH_PARAM_LENGTH);
-      request->outbound_msg_authentication_parameters_offset = pbuf_stream->offset;
-      SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 1, SNMP_V3_MAX_AUTH_PARAM_LENGTH);
-      OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
-      OF_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, request->msg_authentication_parameters, SNMP_V3_MAX_AUTH_PARAM_LENGTH));
-    } else
-#endif
-    {
-      SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, 0);
-      OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
-    }
-
-#if LWIP_SNMP_V3_CRYPTO
-    /* msgPrivacyParameters */
-    if (request->msg_flags & SNMP_V3_PRIV_FLAG) {
-      snmpv3_build_priv_param(request->msg_privacy_parameters);
-
-      SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, SNMP_V3_MAX_PRIV_PARAM_LENGTH);
-      OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
-      OF_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, request->msg_privacy_parameters, SNMP_V3_MAX_PRIV_PARAM_LENGTH));
-    } else
-#endif
-    {
-      SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, 0);
-      OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
-    }
-
-    /* End of msgSecurityParameters, so we can calculate the length of this sequence later */
-    request->outbound_msg_security_parameters_end = pbuf_stream->offset;
-
-#if LWIP_SNMP_V3_CRYPTO
-    /* For encryption we have to encapsulate the payload in an octet string */
-    if (request->msg_flags & SNMP_V3_PRIV_FLAG) {
-      request->outbound_scoped_pdu_string_offset = pbuf_stream->offset;
-      SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 3, 0);
-      OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
-    }
-#endif
-    /* Scoped PDU
-     * Encryption context
-     */
-    request->outbound_scoped_pdu_seq_offset = pbuf_stream->offset;
-    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 3, 0);
-    OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
-
-    /* contextEngineID */
-    snmpv3_get_engine_id(&id, &request->context_engine_id_len);
-    MEMCPY(request->context_engine_id, id, request->context_engine_id_len);
-    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, request->context_engine_id_len);
-    OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
-    OF_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, request->context_engine_id, request->context_engine_id_len));
-
-    /* contextName */
-    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, request->context_name_len);
-    OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
-    OF_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, request->context_name, request->context_name_len));
-  }
-#endif
-
-  /* 'PDU' sequence */
-  request->outbound_pdu_offset = pbuf_stream->offset;
-  SNMP_ASN1_SET_TLV_PARAMS(tlv, request->request_out_type, 3, 0);
-  OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
-
-  /* request ID */
-  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
-  snmp_asn1_enc_s32t_cnt(request->request_id, &tlv.value_len);
-  OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
-  OF_BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, request->request_id) );
-
-  /* error status */
-  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 1);
-  OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
-  request->outbound_error_status_offset = pbuf_stream->offset;
-  OF_BUILD_EXEC( snmp_pbuf_stream_write(pbuf_stream, 0) );
-
-  /* error index */
-  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 1);
-  OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
-  request->outbound_error_index_offset = pbuf_stream->offset;
-  OF_BUILD_EXEC( snmp_pbuf_stream_write(pbuf_stream, 0) );
-
-  /* 'VarBindList' sequence */
-  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 3, 0);
-  OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
-
-  request->outbound_varbind_offset = pbuf_stream->offset;
-
-  return ERR_OK;
-}
-
-/** Calculate the length of a varbind list */
-err_t
-snmp_varbind_length(struct snmp_varbind *varbind, struct snmp_varbind_len *len)
-{
-  /* calculate required lengths */
-  snmp_asn1_enc_oid_cnt(varbind->oid.id, varbind->oid.len, &len->oid_value_len);
-  snmp_asn1_enc_length_cnt(len->oid_value_len, &len->oid_len_len);
-
-  if (varbind->value_len == 0) {
-    len->value_value_len = 0;
-  } else if (varbind->value_len & SNMP_GET_VALUE_RAW_DATA) {
-    len->value_value_len = varbind->value_len & (~SNMP_GET_VALUE_RAW_DATA);
-  } else {
-    switch (varbind->type) {
-      case SNMP_ASN1_TYPE_INTEGER:
-        if (varbind->value_len != sizeof (s32_t)) {
-          return ERR_VAL;
-        }
-        snmp_asn1_enc_s32t_cnt(*((s32_t *) varbind->value), &len->value_value_len);
-        break;
-      case SNMP_ASN1_TYPE_COUNTER:
-      case SNMP_ASN1_TYPE_GAUGE:
-      case SNMP_ASN1_TYPE_TIMETICKS:
-        if (varbind->value_len != sizeof (u32_t)) {
-          return ERR_VAL;
-        }
-        snmp_asn1_enc_u32t_cnt(*((u32_t *) varbind->value), &len->value_value_len);
-        break;
-      case SNMP_ASN1_TYPE_OCTET_STRING:
-      case SNMP_ASN1_TYPE_IPADDR:
-      case SNMP_ASN1_TYPE_OPAQUE:
-        len->value_value_len = varbind->value_len;
-        break;
-      case SNMP_ASN1_TYPE_NULL:
-        if (varbind->value_len != 0) {
-          return ERR_VAL;
-        }
-        len->value_value_len = 0;
-        break;
-      case SNMP_ASN1_TYPE_OBJECT_ID:
-        if ((varbind->value_len & 0x03) != 0) {
-          return ERR_VAL;
-        }
-        snmp_asn1_enc_oid_cnt((u32_t *) varbind->value, varbind->value_len >> 2, &len->value_value_len);
-        break;
-#if LWIP_HAVE_INT64
-      case SNMP_ASN1_TYPE_COUNTER64:
-        if (varbind->value_len != sizeof(u64_t)) {
-          return ERR_VAL;
-        }
-        snmp_asn1_enc_u64t_cnt(*(u64_t *)varbind->value, &len->value_value_len);
-        break;
-#endif
-      default:
-        /* unsupported type */
-        return ERR_VAL;
-    }
-  }
-  snmp_asn1_enc_length_cnt(len->value_value_len, &len->value_len_len);
-
-  len->vb_value_len = 1 + len->oid_len_len + len->oid_value_len + 1 + len->value_len_len + len->value_value_len;
-  snmp_asn1_enc_length_cnt(len->vb_value_len, &len->vb_len_len);
-
-  return ERR_OK;
-}
-
-#define OVB_BUILD_EXEC(code) BUILD_EXEC(code, ERR_ARG)
-
-err_t
-snmp_append_outbound_varbind(struct snmp_pbuf_stream *pbuf_stream, struct snmp_varbind *varbind)
-{
-  struct snmp_asn1_tlv tlv;
-  struct snmp_varbind_len len;
-  err_t err;
-
-  err = snmp_varbind_length(varbind, &len);
-
-  if (err != ERR_OK) {
-    return err;
-  }
-
-  /* check length already before adding first data because in case of GetBulk,
-   *  data added so far is returned and therefore no partial data shall be added
-   */
-  if ((1 + len.vb_len_len + len.vb_value_len) > pbuf_stream->length) {
-    return ERR_BUF;
-  }
-
-  /* 'VarBind' sequence */
-  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, len.vb_len_len, len.vb_value_len);
-  OVB_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
-
-  /* VarBind OID */
-  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OBJECT_ID, len.oid_len_len, len.oid_value_len);
-  OVB_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
-  OVB_BUILD_EXEC(snmp_asn1_enc_oid(pbuf_stream, varbind->oid.id, varbind->oid.len));
-
-  /* VarBind value */
-  SNMP_ASN1_SET_TLV_PARAMS(tlv, varbind->type, len.value_len_len, len.value_value_len);
-  OVB_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
-
-  if (len.value_value_len > 0) {
-    if (varbind->value_len & SNMP_GET_VALUE_RAW_DATA) {
-      OVB_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, (u8_t *) varbind->value, len.value_value_len));
-    } else {
-      switch (varbind->type) {
-        case SNMP_ASN1_TYPE_INTEGER:
-          OVB_BUILD_EXEC(snmp_asn1_enc_s32t(pbuf_stream, len.value_value_len, *((s32_t *) varbind->value)));
-          break;
-        case SNMP_ASN1_TYPE_COUNTER:
-        case SNMP_ASN1_TYPE_GAUGE:
-        case SNMP_ASN1_TYPE_TIMETICKS:
-          OVB_BUILD_EXEC(snmp_asn1_enc_u32t(pbuf_stream, len.value_value_len, *((u32_t *) varbind->value)));
-          break;
-        case SNMP_ASN1_TYPE_OCTET_STRING:
-        case SNMP_ASN1_TYPE_IPADDR:
-        case SNMP_ASN1_TYPE_OPAQUE:
-          OVB_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, (u8_t *) varbind->value, len.value_value_len));
-          len.value_value_len = varbind->value_len;
-          break;
-        case SNMP_ASN1_TYPE_OBJECT_ID:
-          OVB_BUILD_EXEC(snmp_asn1_enc_oid(pbuf_stream, (u32_t *) varbind->value, varbind->value_len / sizeof (u32_t)));
-          break;
-#if LWIP_HAVE_INT64
-        case SNMP_ASN1_TYPE_COUNTER64:
-          OVB_BUILD_EXEC(snmp_asn1_enc_u64t(pbuf_stream, len.value_value_len, *(u64_t *) varbind->value));
-          break;
-#endif
-        default:
-          LWIP_ASSERT("Unknown variable type", 0);
-          break;
-      }
-    }
-  }
-
-  return ERR_OK;
-}
-
-static err_t
-snmp_complete_outbound_frame(struct snmp_request *request)
-{
-  struct snmp_asn1_tlv tlv;
-  u16_t frame_size;
-  u8_t outbound_padding = 0;
-
-  if (request->version == SNMP_VERSION_1) {
-    if (request->error_status != SNMP_ERR_NOERROR) {
-      /* map v2c error codes to v1 compliant error code (according to RFC 2089) */
-      switch (request->error_status) {
-        /* mapping of implementation specific "virtual" error codes
-         * (during processing of frame we already stored them in error_status field,
-         * so no need to check all varbinds here for those exceptions as suggested by RFC) */
-        case SNMP_ERR_NOSUCHINSTANCE:
-        case SNMP_ERR_NOSUCHOBJECT:
-        case SNMP_ERR_ENDOFMIBVIEW:
-          request->error_status = SNMP_ERR_NOSUCHNAME;
-          break;
-        /* mapping according to RFC */
-        case SNMP_ERR_WRONGVALUE:
-        case SNMP_ERR_WRONGENCODING:
-        case SNMP_ERR_WRONGTYPE:
-        case SNMP_ERR_WRONGLENGTH:
-        case SNMP_ERR_INCONSISTENTVALUE:
-          request->error_status = SNMP_ERR_BADVALUE;
-          break;
-        case SNMP_ERR_NOACCESS:
-        case SNMP_ERR_NOTWRITABLE:
-        case SNMP_ERR_NOCREATION:
-        case SNMP_ERR_INCONSISTENTNAME:
-        case SNMP_ERR_AUTHORIZATIONERROR:
-          request->error_status = SNMP_ERR_NOSUCHNAME;
-          break;
-        case SNMP_ERR_RESOURCEUNAVAILABLE:
-        case SNMP_ERR_COMMITFAILED:
-        case SNMP_ERR_UNDOFAILED:
-        default:
-          request->error_status = SNMP_ERR_GENERROR;
-          break;
-      }
-    }
-  } else {
-    if (request->request_type == SNMP_ASN1_CONTEXT_PDU_SET_REQ) {
-      /* map error codes to according to RFC 1905 (4.2.5.  The SetRequest-PDU) return 'NotWritable' for unknown OIDs) */
-      switch (request->error_status) {
-        case SNMP_ERR_NOSUCHINSTANCE:
-        case SNMP_ERR_NOSUCHOBJECT:
-        case SNMP_ERR_ENDOFMIBVIEW:
-          request->error_status = SNMP_ERR_NOTWRITABLE;
-          break;
-        default:
-          break;
-      }
-    }
-
-    if (request->error_status >= SNMP_VARBIND_EXCEPTION_OFFSET) {
-      /* should never occur because v2 frames store exceptions directly inside varbinds and not as frame error_status */
-      LWIP_DEBUGF(SNMP_DEBUG, ("snmp_complete_outbound_frame() > Found v2 request with varbind exception code stored as error status!\n"));
-      return ERR_ARG;
-    }
-  }
-
-  if ((request->error_status != SNMP_ERR_NOERROR) || (request->request_type == SNMP_ASN1_CONTEXT_PDU_SET_REQ)) {
-    /* all inbound vars are returned in response without any modification for error responses and successful set requests*/
-    struct snmp_pbuf_stream inbound_stream;
-    OF_BUILD_EXEC( snmp_pbuf_stream_init(&inbound_stream, request->inbound_pbuf, request->inbound_varbind_offset, request->inbound_varbind_len) );
-    OF_BUILD_EXEC( snmp_pbuf_stream_init(&(request->outbound_pbuf_stream), request->outbound_pbuf, request->outbound_varbind_offset, request->outbound_pbuf->tot_len - request->outbound_varbind_offset) );
-    OF_BUILD_EXEC( snmp_pbuf_stream_writeto(&inbound_stream, &(request->outbound_pbuf_stream), 0) );
-  }
-
-  frame_size = request->outbound_pbuf_stream.offset;
-
-#if LWIP_SNMP_V3 && LWIP_SNMP_V3_CRYPTO
-  /* Calculate padding for encryption */
-  if (request->version == SNMP_VERSION_3 && (request->msg_flags & SNMP_V3_PRIV_FLAG)) {
-    u8_t i;
-    outbound_padding = (8 - (u8_t)((frame_size - request->outbound_scoped_pdu_seq_offset) & 0x07)) & 0x07;
-    for (i = 0; i < outbound_padding; i++) {
-      OF_BUILD_EXEC( snmp_pbuf_stream_write(&request->outbound_pbuf_stream, 0) );
-    }
-  }
-#endif
-
-  /* complete missing length in 'Message' sequence ; 'Message' tlv is located at the beginning (offset 0) */
-  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 3, frame_size + outbound_padding - 1 - 3); /* - type - length_len(fixed, see snmp_prepare_outbound_frame()) */
-  OF_BUILD_EXEC( snmp_pbuf_stream_init(&(request->outbound_pbuf_stream), request->outbound_pbuf, 0, request->outbound_pbuf->tot_len) );
-  OF_BUILD_EXEC( snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv) );
-
-#if LWIP_SNMP_V3
-  if (request->version == SNMP_VERSION_3) {
-    /* complete missing length in 'globalData' sequence */
-    /* - type - length_len(fixed, see snmp_prepare_outbound_frame()) */
-    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 1, request->outbound_msg_global_data_end
-                             - request->outbound_msg_global_data_offset - 1 - 1);
-    OF_BUILD_EXEC(snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_msg_global_data_offset));
-    OF_BUILD_EXEC(snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv));
-
-    /* complete missing length in 'msgSecurityParameters' sequence */
-    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 1, request->outbound_msg_security_parameters_end
-                             - request->outbound_msg_security_parameters_str_offset - 1 - 1);
-    OF_BUILD_EXEC(snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_msg_security_parameters_str_offset));
-    OF_BUILD_EXEC(snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv));
-
-    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 1, request->outbound_msg_security_parameters_end
-                             - request->outbound_msg_security_parameters_seq_offset - 1 - 1);
-    OF_BUILD_EXEC(snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_msg_security_parameters_seq_offset));
-    OF_BUILD_EXEC(snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv));
-
-    /* complete missing length in scoped PDU sequence */
-    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 3, frame_size - request->outbound_scoped_pdu_seq_offset - 1 - 3);
-    OF_BUILD_EXEC(snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_scoped_pdu_seq_offset));
-    OF_BUILD_EXEC(snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv));
-  }
-#endif
-
-  /* complete missing length in 'PDU' sequence */
-  SNMP_ASN1_SET_TLV_PARAMS(tlv, request->request_out_type, 3,
-                           frame_size - request->outbound_pdu_offset - 1 - 3); /* - type - length_len(fixed, see snmp_prepare_outbound_frame()) */
-  OF_BUILD_EXEC( snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_pdu_offset) );
-  OF_BUILD_EXEC( snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv) );
-
-  /* process and encode final error status */
-  if (request->error_status != 0) {
-    u16_t len;
-    snmp_asn1_enc_s32t_cnt(request->error_status, &len);
-    if (len != 1) {
-      /* error, we only reserved one byte for it */
-      return ERR_ARG;
-    }
-    OF_BUILD_EXEC( snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_error_status_offset) );
-    OF_BUILD_EXEC( snmp_asn1_enc_s32t(&(request->outbound_pbuf_stream), len, request->error_status) );
-
-    /* for compatibility to v1, log statistics; in v2 (RFC 1907) these statistics are obsoleted */
-    switch (request->error_status) {
-      case SNMP_ERR_TOOBIG:
-        snmp_stats.outtoobigs++;
-        break;
-      case SNMP_ERR_NOSUCHNAME:
-        snmp_stats.outnosuchnames++;
-        break;
-      case SNMP_ERR_BADVALUE:
-        snmp_stats.outbadvalues++;
-        break;
-      case SNMP_ERR_GENERROR:
-      default:
-        snmp_stats.outgenerrs++;
-        break;
-    }
-
-    if (request->error_status == SNMP_ERR_TOOBIG) {
-      request->error_index = 0; /* defined by RFC 1157 */
-    } else if (request->error_index == 0) {
-      /* set index to varbind where error occured (if not already set before, e.g. during GetBulk processing) */
-      request->error_index = request->inbound_varbind_enumerator.varbind_count;
-    }
-  } else {
-    if (request->request_type == SNMP_ASN1_CONTEXT_PDU_SET_REQ) {
-      snmp_stats.intotalsetvars += request->inbound_varbind_enumerator.varbind_count;
-    } else {
-      snmp_stats.intotalreqvars += request->inbound_varbind_enumerator.varbind_count;
-    }
-  }
-
-  /* encode final error index*/
-  if (request->error_index != 0) {
-    u16_t len;
-    snmp_asn1_enc_s32t_cnt(request->error_index, &len);
-    if (len != 1) {
-      /* error, we only reserved one byte for it */
-      return ERR_VAL;
-    }
-    OF_BUILD_EXEC( snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_error_index_offset) );
-    OF_BUILD_EXEC( snmp_asn1_enc_s32t(&(request->outbound_pbuf_stream), len, request->error_index) );
-  }
-
-  /* complete missing length in 'VarBindList' sequence ; 'VarBindList' tlv is located directly before varbind offset */
-  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 3, frame_size - request->outbound_varbind_offset);
-  OF_BUILD_EXEC( snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_varbind_offset - 1 - 3) ); /* - type - length_len(fixed, see snmp_prepare_outbound_frame()) */
-  OF_BUILD_EXEC( snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv) );
-
-  /* Authenticate response */
-#if LWIP_SNMP_V3 && LWIP_SNMP_V3_CRYPTO
-  /* Encrypt response */
-  if (request->version == SNMP_VERSION_3 && (request->msg_flags & SNMP_V3_PRIV_FLAG)) {
-    u8_t key[20];
-    snmpv3_priv_algo_t algo;
-
-    /* complete missing length in PDU sequence */
-    OF_BUILD_EXEC(snmp_pbuf_stream_init(&request->outbound_pbuf_stream, request->outbound_pbuf, 0, request->outbound_pbuf->tot_len));
-    OF_BUILD_EXEC(snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_scoped_pdu_string_offset));
-    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 3, frame_size + outbound_padding
-                             - request->outbound_scoped_pdu_string_offset - 1 - 3);
-    OF_BUILD_EXEC(snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv));
-
-    OF_BUILD_EXEC(snmpv3_get_user((char *)request->msg_user_name, NULL, NULL, &algo, key));
-
-    OF_BUILD_EXEC(snmpv3_crypt(&request->outbound_pbuf_stream, tlv.value_len, key,
-                               request->msg_privacy_parameters, request->msg_authoritative_engine_boots,
-                               request->msg_authoritative_engine_time, algo, SNMP_V3_PRIV_MODE_ENCRYPT));
-  }
-
-  if (request->version == SNMP_VERSION_3 && (request->msg_flags & SNMP_V3_AUTH_FLAG)) {
-    u8_t key[20];
-    snmpv3_auth_algo_t algo;
-    u8_t hmac[20];
-
-    OF_BUILD_EXEC(snmpv3_get_user((char *)request->msg_user_name, &algo, key, NULL, NULL));
-    OF_BUILD_EXEC(snmp_pbuf_stream_init(&(request->outbound_pbuf_stream),
-                                        request->outbound_pbuf, 0, request->outbound_pbuf->tot_len));
-    OF_BUILD_EXEC(snmpv3_auth(&request->outbound_pbuf_stream, frame_size + outbound_padding, key, algo, hmac));
-
-    MEMCPY(request->msg_authentication_parameters, hmac, SNMP_V3_MAX_AUTH_PARAM_LENGTH);
-    OF_BUILD_EXEC(snmp_pbuf_stream_init(&request->outbound_pbuf_stream,
-                                        request->outbound_pbuf, 0, request->outbound_pbuf->tot_len));
-    OF_BUILD_EXEC(snmp_pbuf_stream_seek_abs(&request->outbound_pbuf_stream,
-                                            request->outbound_msg_authentication_parameters_offset));
-
-    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 1, SNMP_V3_MAX_AUTH_PARAM_LENGTH);
-    OF_BUILD_EXEC(snmp_ans1_enc_tlv(&request->outbound_pbuf_stream, &tlv));
-    OF_BUILD_EXEC(snmp_asn1_enc_raw(&request->outbound_pbuf_stream,
-                                    request->msg_authentication_parameters, SNMP_V3_MAX_AUTH_PARAM_LENGTH));
-  }
-#endif
-
-  pbuf_realloc(request->outbound_pbuf, frame_size + outbound_padding);
-
-  snmp_stats.outgetresponses++;
-  snmp_stats.outpkts++;
-
-  return ERR_OK;
-}
-
-static void
-snmp_execute_write_callbacks(struct snmp_request *request)
-{
-  struct snmp_varbind_enumerator inbound_varbind_enumerator;
-  struct snmp_varbind vb;
-
-  snmp_vb_enumerator_init(&inbound_varbind_enumerator, request->inbound_pbuf, request->inbound_varbind_offset, request->inbound_varbind_len);
-  vb.value = NULL; /* do NOT decode value (we enumerate outbound buffer here, so all varbinds have values assigned, which we don't need here) */
-
-  while (snmp_vb_enumerator_get_next(&inbound_varbind_enumerator, &vb) == SNMP_VB_ENUMERATOR_ERR_OK) {
-    snmp_write_callback(vb.oid.id, vb.oid.len, snmp_write_callback_arg);
-  }
-}
-
-
-/* ----------------------------------------------------------------------- */
-/* VarBind enumerator methods */
-/* ----------------------------------------------------------------------- */
-
-void
-snmp_vb_enumerator_init(struct snmp_varbind_enumerator *enumerator, struct pbuf *p, u16_t offset, u16_t length)
-{
-  snmp_pbuf_stream_init(&(enumerator->pbuf_stream), p, offset, length);
-  enumerator->varbind_count = 0;
-}
-
-#define VB_PARSE_EXEC(code)   PARSE_EXEC(code, SNMP_VB_ENUMERATOR_ERR_ASN1ERROR)
-#define VB_PARSE_ASSERT(code) PARSE_ASSERT(code, SNMP_VB_ENUMERATOR_ERR_ASN1ERROR)
-
-snmp_vb_enumerator_err_t
-snmp_vb_enumerator_get_next(struct snmp_varbind_enumerator *enumerator, struct snmp_varbind *varbind)
-{
-  struct snmp_asn1_tlv tlv;
-  u16_t  varbind_len;
-  err_t  err;
-
-  if (enumerator->pbuf_stream.length == 0) {
-    return SNMP_VB_ENUMERATOR_ERR_EOVB;
-  }
-  enumerator->varbind_count++;
-
-  /* decode varbind itself (parent container of a varbind) */
-  VB_PARSE_EXEC(snmp_asn1_dec_tlv(&(enumerator->pbuf_stream), &tlv));
-  VB_PARSE_ASSERT((tlv.type == SNMP_ASN1_TYPE_SEQUENCE) && (tlv.value_len <= enumerator->pbuf_stream.length));
-  varbind_len = tlv.value_len;
-
-  /* decode varbind name (object id) */
-  VB_PARSE_EXEC(snmp_asn1_dec_tlv(&(enumerator->pbuf_stream), &tlv));
-  VB_PARSE_ASSERT((tlv.type == SNMP_ASN1_TYPE_OBJECT_ID) && (SNMP_ASN1_TLV_LENGTH(tlv) < varbind_len) && (tlv.value_len < enumerator->pbuf_stream.length));
-
-  VB_PARSE_EXEC(snmp_asn1_dec_oid(&(enumerator->pbuf_stream), tlv.value_len, varbind->oid.id, &(varbind->oid.len), SNMP_MAX_OBJ_ID_LEN));
-  varbind_len -= SNMP_ASN1_TLV_LENGTH(tlv);
-
-  /* decode varbind value (object id) */
-  VB_PARSE_EXEC(snmp_asn1_dec_tlv(&(enumerator->pbuf_stream), &tlv));
-  VB_PARSE_ASSERT((SNMP_ASN1_TLV_LENGTH(tlv) == varbind_len) && (tlv.value_len <= enumerator->pbuf_stream.length));
-  varbind->type = tlv.type;
-
-  /* shall the value be decoded ? */
-  if (varbind->value != NULL) {
-    switch (varbind->type) {
-      case SNMP_ASN1_TYPE_INTEGER:
-        VB_PARSE_EXEC(snmp_asn1_dec_s32t(&(enumerator->pbuf_stream), tlv.value_len, (s32_t *)varbind->value));
-        varbind->value_len = sizeof(s32_t);
-        break;
-      case SNMP_ASN1_TYPE_COUNTER:
-      case SNMP_ASN1_TYPE_GAUGE:
-      case SNMP_ASN1_TYPE_TIMETICKS:
-        VB_PARSE_EXEC(snmp_asn1_dec_u32t(&(enumerator->pbuf_stream), tlv.value_len, (u32_t *)varbind->value));
-        varbind->value_len = sizeof(u32_t);
-        break;
-      case SNMP_ASN1_TYPE_OCTET_STRING:
-      case SNMP_ASN1_TYPE_OPAQUE:
-        err = snmp_asn1_dec_raw(&(enumerator->pbuf_stream), tlv.value_len, (u8_t *)varbind->value, &varbind->value_len, SNMP_MAX_VALUE_SIZE);
-        if (err == ERR_MEM) {
-          return SNMP_VB_ENUMERATOR_ERR_INVALIDLENGTH;
-        }
-        VB_PARSE_ASSERT(err == ERR_OK);
-        break;
-      case SNMP_ASN1_TYPE_NULL:
-        varbind->value_len = 0;
-        break;
-      case SNMP_ASN1_TYPE_OBJECT_ID:
-        /* misuse tlv.length_len as OID_length transporter */
-        err = snmp_asn1_dec_oid(&(enumerator->pbuf_stream), tlv.value_len, (u32_t *)varbind->value, &tlv.length_len, SNMP_MAX_OBJ_ID_LEN);
-        if (err == ERR_MEM) {
-          return SNMP_VB_ENUMERATOR_ERR_INVALIDLENGTH;
-        }
-        VB_PARSE_ASSERT(err == ERR_OK);
-        varbind->value_len = tlv.length_len * sizeof(u32_t);
-        break;
-      case SNMP_ASN1_TYPE_IPADDR:
-        if (tlv.value_len == 4) {
-          /* must be exactly 4 octets! */
-          VB_PARSE_EXEC(snmp_asn1_dec_raw(&(enumerator->pbuf_stream), tlv.value_len, (u8_t *)varbind->value, &varbind->value_len, SNMP_MAX_VALUE_SIZE));
-        } else {
-          VB_PARSE_ASSERT(0);
-        }
-        break;
-#if LWIP_HAVE_INT64
-      case SNMP_ASN1_TYPE_COUNTER64:
-        VB_PARSE_EXEC(snmp_asn1_dec_u64t(&(enumerator->pbuf_stream), tlv.value_len, (u64_t *)varbind->value));
-        varbind->value_len = sizeof(u64_t);
-        break;
-#endif
-      default:
-        VB_PARSE_ASSERT(0);
-        break;
-    }
-  } else {
-    snmp_pbuf_stream_seek(&(enumerator->pbuf_stream), tlv.value_len);
-    varbind->value_len = tlv.value_len;
-  }
-
-  return SNMP_VB_ENUMERATOR_ERR_OK;
-}
-
-#endif /* LWIP_SNMP */
+/**
+ * @file
+ * SNMP message processing (RFC1157).
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * Copyright (c) 2016 Elias Oenal.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Christiaan Simons <christiaan.simons@axon.tv>
+ *         Martin Hentschel <info@cl-soft.de>
+ *         Elias Oenal <lwip@eliasoenal.com>
+ */
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "snmp_msg.h"
+#include "snmp_asn1.h"
+#include "snmp_core_priv.h"
+#include "lwip/ip_addr.h"
+#include "lwip/stats.h"
+
+#if LWIP_SNMP_V3
+#include "lwip/apps/snmpv3.h"
+#include "snmpv3_priv.h"
+#ifdef LWIP_SNMPV3_INCLUDE_ENGINE
+#include LWIP_SNMPV3_INCLUDE_ENGINE
+#endif
+#endif
+
+#include <string.h>
+
+/* public (non-static) constants */
+/** SNMP community string */
+const char *snmp_community = SNMP_COMMUNITY;
+/** SNMP community string for write access */
+const char *snmp_community_write = SNMP_COMMUNITY_WRITE;
+/** SNMP community string for sending traps */
+const char *snmp_community_trap = SNMP_COMMUNITY_TRAP;
+
+snmp_write_callback_fct snmp_write_callback     = NULL;
+void*                   snmp_write_callback_arg = NULL;
+
+/**
+ * @ingroup snmp_core
+ * Returns current SNMP community string.
+ * @return current SNMP community string
+ */
+const char *
+snmp_get_community(void)
+{
+  return snmp_community;
+}
+
+/**
+ * @ingroup snmp_core
+ * Sets SNMP community string.
+ * The string itself (its storage) must be valid throughout the whole life of
+ * program (or until it is changed to sth else).
+ *
+ * @param community is a pointer to new community string
+ */
+void
+snmp_set_community(const char * const community)
+{
+  LWIP_ASSERT("community string is too long!", strlen(community) <= SNMP_MAX_COMMUNITY_STR_LEN);
+  snmp_community = community;
+}
+
+/**
+ * @ingroup snmp_core
+ * Returns current SNMP write-access community string.
+ * @return current SNMP write-access community string
+ */
+const char *
+snmp_get_community_write(void)
+{
+  return snmp_community_write;
+}
+
+/**
+ * @ingroup snmp_traps
+ * Returns current SNMP community string used for sending traps.
+ * @return current SNMP community string used for sending traps
+ */
+const char *
+snmp_get_community_trap(void)
+{
+  return snmp_community_trap;
+}
+
+/**
+ * @ingroup snmp_core
+ * Sets SNMP community string for write-access.
+ * The string itself (its storage) must be valid throughout the whole life of
+ * program (or until it is changed to sth else).
+ *
+ * @param community is a pointer to new write-access community string
+ */
+void
+snmp_set_community_write(const char * const community)
+{
+  LWIP_ASSERT("community string must not be NULL", community != NULL);
+  LWIP_ASSERT("community string is too long!", strlen(community) <= SNMP_MAX_COMMUNITY_STR_LEN);
+  snmp_community_write = community;
+}
+
+/**
+ * @ingroup snmp_traps
+ * Sets SNMP community string used for sending traps.
+ * The string itself (its storage) must be valid throughout the whole life of
+ * program (or until it is changed to sth else).
+ *
+ * @param community is a pointer to new trap community string
+ */
+void
+snmp_set_community_trap(const char * const community)
+{
+  LWIP_ASSERT("community string is too long!", strlen(community) <= SNMP_MAX_COMMUNITY_STR_LEN);
+  snmp_community_trap = community;
+}
+
+/**
+ * @ingroup snmp_core
+ * Callback fired on every successful write access
+ */
+void 
+snmp_set_write_callback(snmp_write_callback_fct write_callback, void* callback_arg)
+{
+  snmp_write_callback     = write_callback;
+  snmp_write_callback_arg = callback_arg;
+}
+
+/* ----------------------------------------------------------------------- */
+/* forward declarations */
+/* ----------------------------------------------------------------------- */
+
+static err_t snmp_process_get_request(struct snmp_request *request);
+static err_t snmp_process_getnext_request(struct snmp_request *request);
+static err_t snmp_process_getbulk_request(struct snmp_request *request);
+static err_t snmp_process_set_request(struct snmp_request *request);
+
+static err_t snmp_parse_inbound_frame(struct snmp_request *request);
+static err_t snmp_prepare_outbound_frame(struct snmp_request *request);
+static err_t snmp_complete_outbound_frame(struct snmp_request *request);
+static void snmp_execute_write_callbacks(struct snmp_request *request);
+
+
+/* ----------------------------------------------------------------------- */
+/* implementation */
+/* ----------------------------------------------------------------------- */
+
+void
+snmp_receive(void *handle, struct pbuf *p, const ip_addr_t *source_ip, u16_t port)
+{
+  err_t err;
+  struct snmp_request request;
+   
+  memset(&request, 0, sizeof(request));
+  request.handle       = handle;
+  request.source_ip    = source_ip;
+  request.source_port  = port;
+  request.inbound_pbuf = p;
+
+  snmp_stats.inpkts++;
+
+  err = snmp_parse_inbound_frame(&request);
+  if (err == ERR_OK) {
+    err = snmp_prepare_outbound_frame(&request);
+    if (err == ERR_OK) {
+
+      if (request.error_status == SNMP_ERR_NOERROR) {
+        /* only process frame if we do not already have an error to return (e.g. all readonly) */
+        if (request.request_type == SNMP_ASN1_CONTEXT_PDU_GET_REQ) {
+          err = snmp_process_get_request(&request);
+        } else if (request.request_type == SNMP_ASN1_CONTEXT_PDU_GET_NEXT_REQ) {
+          err = snmp_process_getnext_request(&request);
+        } else if (request.request_type == SNMP_ASN1_CONTEXT_PDU_GET_BULK_REQ) {
+          err = snmp_process_getbulk_request(&request);
+        } else if (request.request_type == SNMP_ASN1_CONTEXT_PDU_SET_REQ) {
+          err = snmp_process_set_request(&request);
+        }
+      }
+
+      if (err == ERR_OK) {
+        err = snmp_complete_outbound_frame(&request);
+      
+        if (err == ERR_OK) {
+          err = snmp_sendto(request.handle, request.outbound_pbuf, request.source_ip, request.source_port);
+
+          if ((request.request_type == SNMP_ASN1_CONTEXT_PDU_SET_REQ) 
+            && (request.error_status == SNMP_ERR_NOERROR) 
+            && (snmp_write_callback != NULL)) {
+            /* raise write notification for all written objects */
+            snmp_execute_write_callbacks(&request);
+          }
+        }
+      }
+    }
+  
+    if (request.outbound_pbuf != NULL) {
+      pbuf_free(request.outbound_pbuf);
+    }
+  }
+}
+
+static u8_t
+snmp_msg_getnext_validate_node_inst(struct snmp_node_instance* node_instance, void* validate_arg)
+{
+  if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_READ) != SNMP_NODE_INSTANCE_ACCESS_READ) || (node_instance->get_value == NULL)) {
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  if ((node_instance->asn1_type == SNMP_ASN1_TYPE_COUNTER64) && (((struct snmp_request*)validate_arg)->version == SNMP_VERSION_1)) {
+    /* according to RFC 2089 skip Counter64 objects in GetNext requests from v1 clients */
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  return SNMP_ERR_NOERROR;
+}
+
+static void 
+snmp_process_varbind(struct snmp_request *request, struct snmp_varbind *vb, u8_t get_next)
+{
+  err_t err;
+  struct snmp_node_instance node_instance;
+  memset(&node_instance, 0, sizeof(node_instance));
+
+  if (get_next) {
+    struct snmp_obj_id result_oid;
+    request->error_status = snmp_get_next_node_instance_from_oid(vb->oid.id, vb->oid.len, snmp_msg_getnext_validate_node_inst, request,  &result_oid, &node_instance);
+
+    if (request->error_status == SNMP_ERR_NOERROR) {
+      snmp_oid_assign(&vb->oid, result_oid.id, result_oid.len);
+    }
+  } else {
+    request->error_status = snmp_get_node_instance_from_oid(vb->oid.id, vb->oid.len, &node_instance);
+
+    if (request->error_status == SNMP_ERR_NOERROR) {
+      /* use 'getnext_validate' method for validation to avoid code duplication (some checks have to be executed here) */
+      request->error_status = snmp_msg_getnext_validate_node_inst(&node_instance, request);
+
+      if (request->error_status != SNMP_ERR_NOERROR) {
+        if (node_instance.release_instance != NULL) {
+          node_instance.release_instance(&node_instance);
+        }
+      }
+    }
+  }
+
+  if (request->error_status != SNMP_ERR_NOERROR)  {
+    if (request->error_status >= SNMP_VARBIND_EXCEPTION_OFFSET) {
+      if ((request->version == SNMP_VERSION_2c) || request->version == SNMP_VERSION_3) {
+        /* in SNMP v2c a varbind related exception is stored in varbind and not in frame header */
+        vb->type = (SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_CLASS_CONTEXT | (request->error_status & SNMP_VARBIND_EXCEPTION_MASK));
+        vb->value_len = 0;
+
+        err = snmp_append_outbound_varbind(&(request->outbound_pbuf_stream), vb);
+        if (err == ERR_OK) {
+          /* we stored the exception in varbind -> go on */
+          request->error_status = SNMP_ERR_NOERROR;
+        } else if (err == ERR_BUF) {
+          request->error_status = SNMP_ERR_TOOBIG;
+        } else {
+          request->error_status = SNMP_ERR_GENERROR;
+        }
+      }
+    } else {
+      /* according to RFC 1157/1905, all other errors only return genError */
+      request->error_status = SNMP_ERR_GENERROR;
+    }
+  } else {
+    s16_t len = node_instance.get_value(&node_instance, vb->value);
+    vb->type = node_instance.asn1_type;
+
+    if(len >= 0) {
+      vb->value_len = (u16_t)len; /* cast is OK because we checked >= 0 above */
+
+      LWIP_ASSERT("SNMP_MAX_VALUE_SIZE is configured too low", (vb->value_len & ~SNMP_GET_VALUE_RAW_DATA) <= SNMP_MAX_VALUE_SIZE);
+      err = snmp_append_outbound_varbind(&request->outbound_pbuf_stream, vb);
+
+      if (err == ERR_BUF) {
+        request->error_status = SNMP_ERR_TOOBIG;
+      } else if (err != ERR_OK) {
+        request->error_status = SNMP_ERR_GENERROR;
+      }
+    } else {
+      request->error_status = SNMP_ERR_GENERROR;
+    }
+
+    if (node_instance.release_instance != NULL) {
+      node_instance.release_instance(&node_instance);
+    }
+  }
+}
+
+
+/**
+ * Service an internal or external event for SNMP GET.
+ *
+ * @param request points to the associated message process state
+ */
+static err_t
+snmp_process_get_request(struct snmp_request *request)
+{
+  snmp_vb_enumerator_err_t err;
+  struct snmp_varbind vb;
+  vb.value = request->value_buffer;
+
+  LWIP_DEBUGF(SNMP_DEBUG, ("SNMP get request\n"));
+
+  while (request->error_status == SNMP_ERR_NOERROR) {
+    err = snmp_vb_enumerator_get_next(&request->inbound_varbind_enumerator, &vb);
+    if (err == SNMP_VB_ENUMERATOR_ERR_OK) {
+      if ((vb.type == SNMP_ASN1_TYPE_NULL) && (vb.value_len == 0)) {
+        snmp_process_varbind(request, &vb, 0);
+      } else {
+        request->error_status = SNMP_ERR_GENERROR;
+      }
+    } else if (err == SNMP_VB_ENUMERATOR_ERR_EOVB) {
+      /* no more varbinds in request */
+      break;
+    } else if (err == SNMP_VB_ENUMERATOR_ERR_ASN1ERROR) {
+      /* malformed ASN.1, don't answer */
+      return ERR_ARG;
+    } else {
+      request->error_status = SNMP_ERR_GENERROR;
+    }
+  }
+
+  return ERR_OK;
+}
+
+/**
+ * Service an internal or external event for SNMP GET.
+ *
+ * @param request points to the associated message process state
+ */
+static err_t
+snmp_process_getnext_request(struct snmp_request *request)
+{
+  snmp_vb_enumerator_err_t err;
+  struct snmp_varbind vb;
+  vb.value = request->value_buffer;
+
+  LWIP_DEBUGF(SNMP_DEBUG, ("SNMP get-next request\n"));
+
+  while (request->error_status == SNMP_ERR_NOERROR) {
+    err = snmp_vb_enumerator_get_next(&request->inbound_varbind_enumerator, &vb);
+    if (err == SNMP_VB_ENUMERATOR_ERR_OK) {
+      if ((vb.type == SNMP_ASN1_TYPE_NULL) && (vb.value_len == 0)) {
+        snmp_process_varbind(request, &vb, 1);
+      } else {
+        request->error_status = SNMP_ERR_GENERROR;
+      }
+    } else if (err == SNMP_VB_ENUMERATOR_ERR_EOVB) {
+      /* no more varbinds in request */
+      break;
+    } else if (err == SNMP_VB_ENUMERATOR_ERR_ASN1ERROR) {
+      /* malformed ASN.1, don't answer */
+      return ERR_ARG;
+    } else {
+      request->error_status = SNMP_ERR_GENERROR;
+    }
+  }
+  
+  return ERR_OK;
+}
+
+/**
+ * Service an internal or external event for SNMP GETBULKT.
+ *
+ * @param request points to the associated message process state
+ */
+static err_t
+snmp_process_getbulk_request(struct snmp_request *request)
+{
+  snmp_vb_enumerator_err_t err;
+  s32_t non_repeaters     = request->non_repeaters;
+  s32_t repetitions;
+  u16_t repetition_offset = 0;
+  struct snmp_varbind_enumerator repetition_varbind_enumerator;
+  struct snmp_varbind vb;
+  vb.value = request->value_buffer;
+
+  if (SNMP_LWIP_GETBULK_MAX_REPETITIONS > 0) {
+    repetitions = LWIP_MIN(request->max_repetitions, SNMP_LWIP_GETBULK_MAX_REPETITIONS);
+  } else {
+    repetitions = request->max_repetitions;
+  }
+
+  LWIP_DEBUGF(SNMP_DEBUG, ("SNMP get-bulk request\n"));
+
+  /* process non repeaters and first repetition */
+  while (request->error_status == SNMP_ERR_NOERROR) {
+    if (non_repeaters == 0) {
+      repetition_offset = request->outbound_pbuf_stream.offset;
+
+      if (repetitions == 0) {
+        /* do not resolve repeaters when repetitions is set to 0 */
+        break;
+      }
+      repetitions--;
+    }
+
+    err = snmp_vb_enumerator_get_next(&request->inbound_varbind_enumerator, &vb);
+    if (err == SNMP_VB_ENUMERATOR_ERR_EOVB) {
+      /* no more varbinds in request */
+      break;
+    } else if (err == SNMP_VB_ENUMERATOR_ERR_ASN1ERROR) {
+      /* malformed ASN.1, don't answer */
+      return ERR_ARG;
+    } else if ((err != SNMP_VB_ENUMERATOR_ERR_OK) || (vb.type != SNMP_ASN1_TYPE_NULL) || (vb.value_len != 0)) {
+      request->error_status = SNMP_ERR_GENERROR;
+    } else {
+      snmp_process_varbind(request, &vb, 1);
+      non_repeaters--;
+    }
+  }
+
+  /* process repetitions > 1 */
+  while ((request->error_status == SNMP_ERR_NOERROR) && (repetitions > 0) && (request->outbound_pbuf_stream.offset != repetition_offset)) {
+
+    u8_t all_endofmibview = 1;
+    
+    snmp_vb_enumerator_init(&repetition_varbind_enumerator, request->outbound_pbuf, repetition_offset, request->outbound_pbuf_stream.offset - repetition_offset);
+    repetition_offset = request->outbound_pbuf_stream.offset; /* for next loop */
+
+    while (request->error_status == SNMP_ERR_NOERROR) {
+      vb.value = NULL; /* do NOT decode value (we enumerate outbound buffer here, so all varbinds have values assigned) */
+      err = snmp_vb_enumerator_get_next(&repetition_varbind_enumerator, &vb);
+      if (err == SNMP_VB_ENUMERATOR_ERR_OK) {
+        vb.value = request->value_buffer;
+        snmp_process_varbind(request, &vb, 1);
+
+        if (request->error_status != SNMP_ERR_NOERROR) {
+          /* already set correct error-index (here it cannot be taken from inbound varbind enumerator) */
+          request->error_index = request->non_repeaters + repetition_varbind_enumerator.varbind_count;
+        } else if (vb.type != (SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTEXT_VARBIND_END_OF_MIB_VIEW)) {
+          all_endofmibview = 0;
+        }
+      } else if (err == SNMP_VB_ENUMERATOR_ERR_EOVB) {
+        /* no more varbinds in request */
+        break;
+      } else {
+        LWIP_DEBUGF(SNMP_DEBUG, ("Very strange, we cannot parse the varbind output that we created just before!"));
+        request->error_status = SNMP_ERR_GENERROR;
+        request->error_index  = request->non_repeaters + repetition_varbind_enumerator.varbind_count;
+      }
+    }
+
+    if ((request->error_status == SNMP_ERR_NOERROR) && all_endofmibview) {
+      /* stop when all varbinds in a loop return EndOfMibView */
+      break;
+    }
+    
+    repetitions--;
+  }
+
+  if (request->error_status == SNMP_ERR_TOOBIG) {
+    /* for GetBulk it is ok, if not all requested variables fit into the response -> just return the varbinds added so far */
+    request->error_status = SNMP_ERR_NOERROR;
+  }
+
+  return ERR_OK;
+}
+
+/**
+ * Service an internal or external event for SNMP SET.
+ *
+ * @param request points to the associated message process state
+ */
+static err_t
+snmp_process_set_request(struct snmp_request *request)
+{
+  snmp_vb_enumerator_err_t err;
+  struct snmp_varbind vb;
+  vb.value = request->value_buffer;
+
+  LWIP_DEBUGF(SNMP_DEBUG, ("SNMP set request\n"));
+
+  /* perform set test on all objects */
+  while (request->error_status == SNMP_ERR_NOERROR) {
+    err = snmp_vb_enumerator_get_next(&request->inbound_varbind_enumerator, &vb);
+    if (err == SNMP_VB_ENUMERATOR_ERR_OK) {
+      struct snmp_node_instance node_instance;
+      memset(&node_instance, 0, sizeof(node_instance));
+      
+      request->error_status = snmp_get_node_instance_from_oid(vb.oid.id, vb.oid.len, &node_instance);
+      if (request->error_status == SNMP_ERR_NOERROR) {
+        if (node_instance.asn1_type != vb.type) {
+          request->error_status = SNMP_ERR_WRONGTYPE;
+        } else if (((node_instance.access & SNMP_NODE_INSTANCE_ACCESS_WRITE) != SNMP_NODE_INSTANCE_ACCESS_WRITE) || (node_instance.set_value == NULL)) {
+          request->error_status = SNMP_ERR_NOTWRITABLE;
+        } else {
+          if (node_instance.set_test != NULL) {
+            request->error_status = node_instance.set_test(&node_instance, vb.value_len, vb.value);
+          }
+        }
+
+        if (node_instance.release_instance != NULL) {
+          node_instance.release_instance(&node_instance);
+        }
+      }
+    } else if (err == SNMP_VB_ENUMERATOR_ERR_EOVB) {
+      /* no more varbinds in request */
+      break;
+    } else if (err == SNMP_VB_ENUMERATOR_ERR_INVALIDLENGTH) {
+      request->error_status = SNMP_ERR_WRONGLENGTH;
+    } else if (err == SNMP_VB_ENUMERATOR_ERR_ASN1ERROR) {
+      /* malformed ASN.1, don't answer */
+      return ERR_ARG;
+    } else {
+      request->error_status = SNMP_ERR_GENERROR;
+    }
+  }
+
+  /* perform real set operation on all objects */
+  if (request->error_status == SNMP_ERR_NOERROR) {
+    snmp_vb_enumerator_init(&request->inbound_varbind_enumerator, request->inbound_pbuf, request->inbound_varbind_offset, request->inbound_varbind_len);
+    while (request->error_status == SNMP_ERR_NOERROR) {
+      err = snmp_vb_enumerator_get_next(&request->inbound_varbind_enumerator, &vb);
+      if (err == SNMP_VB_ENUMERATOR_ERR_OK) {
+        struct snmp_node_instance node_instance;
+        memset(&node_instance, 0, sizeof(node_instance));
+        request->error_status = snmp_get_node_instance_from_oid(vb.oid.id, vb.oid.len, &node_instance);
+        if (request->error_status == SNMP_ERR_NOERROR) {
+          if (node_instance.set_value(&node_instance, vb.value_len, vb.value) != SNMP_ERR_NOERROR) {
+            if (request->inbound_varbind_enumerator.varbind_count == 1) {
+              request->error_status = SNMP_ERR_COMMITFAILED;
+            } else {
+              /* we cannot undo the set operations done so far */
+              request->error_status = SNMP_ERR_UNDOFAILED;
+            }
+          }
+
+          if (node_instance.release_instance != NULL) {
+            node_instance.release_instance(&node_instance);
+          }
+        }
+      } else if (err == SNMP_VB_ENUMERATOR_ERR_EOVB) {
+        /* no more varbinds in request */
+        break;
+      } else {
+        /* first time enumerating varbinds work but second time not, although nothing should have changed in between ??? */
+        request->error_status = SNMP_ERR_GENERROR;
+      }
+    }
+  }
+
+  return ERR_OK;
+}
+
+#define PARSE_EXEC(code, retValue) \
+  if ((code) != ERR_OK) { \
+    LWIP_DEBUGF(SNMP_DEBUG, ("Malformed ASN.1 detected.\n")); \
+    snmp_stats.inasnparseerrs++; \
+    return retValue; \
+  }
+
+#define PARSE_ASSERT(cond, retValue) \
+  if (!(cond)) { \
+    LWIP_DEBUGF(SNMP_DEBUG, ("SNMP parse assertion failed!: " # cond)); \
+    snmp_stats.inasnparseerrs++; \
+    return retValue; \
+  }
+
+#define BUILD_EXEC(code, retValue) \
+  if ((code) != ERR_OK) { \
+    LWIP_DEBUGF(SNMP_DEBUG, ("SNMP error during creation of outbound frame!: " # code)); \
+    return retValue; \
+  }
+
+#define IF_PARSE_EXEC(code)   PARSE_EXEC(code, ERR_ARG)
+#define IF_PARSE_ASSERT(code) PARSE_ASSERT(code, ERR_ARG)
+
+/**
+ * Checks and decodes incoming SNMP message header, logs header errors.
+ *
+ * @param request points to the current message request state return
+ * @return
+ * - ERR_OK SNMP header is sane and accepted
+ * - ERR_VAL SNMP header is either malformed or rejected
+ */
+static err_t
+snmp_parse_inbound_frame(struct snmp_request *request)
+{
+  struct snmp_pbuf_stream pbuf_stream;
+  struct snmp_asn1_tlv tlv;
+  s32_t parent_tlv_value_len;
+  s32_t s32_value;
+  err_t err;
+
+  IF_PARSE_EXEC(snmp_pbuf_stream_init(&pbuf_stream, request->inbound_pbuf, 0, request->inbound_pbuf->tot_len));
+  
+  /* decode main container consisting of version, community and PDU */
+  IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+  IF_PARSE_ASSERT((tlv.type == SNMP_ASN1_TYPE_SEQUENCE) && (tlv.value_len == pbuf_stream.length));
+  parent_tlv_value_len = tlv.value_len;
+
+  /* decode version */
+  IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+  IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER);
+  parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+  IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+  
+  IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &s32_value));
+  if ((s32_value != SNMP_VERSION_1) &&
+      (s32_value != SNMP_VERSION_2c)
+#if LWIP_SNMP_V3
+      && (s32_value != SNMP_VERSION_3)
+#endif
+     )
+  {
+    /* unsupported SNMP version */
+    snmp_stats.inbadversions++;
+    return ERR_ARG;
+  }
+  request->version = (u8_t)s32_value;
+
+#if LWIP_SNMP_V3
+  if (request->version == SNMP_VERSION_3) {
+    u16_t u16_value;
+    u16_t inbound_msgAuthenticationParameters_offset;
+
+    /* SNMPv3 doesn't use communities */
+    /* @todo: Differentiate read/write access */
+    strcpy((char*)request->community, snmp_community);
+    request->community_strlen = (u16_t)strlen(snmp_community);
+
+    /* RFC3414 globalData */
+    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_SEQUENCE);
+    parent_tlv_value_len -= SNMP_ASN1_TLV_HDR_LENGTH(tlv);
+    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+    /* decode msgID */
+    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER);
+    parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+    IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &s32_value));
+    request->msg_id = s32_value;
+
+    /* decode msgMaxSize */
+    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER);
+    parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+    IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &s32_value));
+    request->msg_max_size = s32_value;
+
+    /* decode msgFlags */
+    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
+    parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+    IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &s32_value));
+    request->msg_flags = (u8_t)s32_value;
+
+    /* decode msgSecurityModel */
+    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER);
+    parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+    IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &s32_value));
+    request->msg_security_model = s32_value;
+
+    /* RFC3414 msgSecurityParameters
+     * The User-based Security Model defines the contents of the OCTET
+     * STRING as a SEQUENCE.
+     *
+     * We skip the protective dummy OCTET STRING header
+     * to access the SEQUENCE header.
+     */
+    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
+    parent_tlv_value_len -= SNMP_ASN1_TLV_HDR_LENGTH(tlv);
+    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+    /* msgSecurityParameters SEQUENCE header */
+    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_SEQUENCE);
+    parent_tlv_value_len -= SNMP_ASN1_TLV_HDR_LENGTH(tlv);
+    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+    /* decode msgAuthoritativeEngineID */
+    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
+    parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+    IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->msg_authoritative_engine_id,
+        &u16_value, SNMP_V3_MAX_ENGINE_ID_LENGTH));
+    request->msg_authoritative_engine_id_len = (u8_t)u16_value;
+
+    /* msgAuthoritativeEngineBoots */
+    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER);
+    parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+    IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &request->msg_authoritative_engine_boots));
+
+    /* msgAuthoritativeEngineTime */
+    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER);
+    parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+    IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &request->msg_authoritative_engine_time));
+    /* @todo: Implement time window checking */
+
+    /* msgUserName */
+    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
+    parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+    IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->msg_user_name,
+        &u16_value, SNMP_V3_MAX_USER_LENGTH));
+    request->msg_user_name_len = (u8_t)u16_value;
+    /* @todo: Implement unknown user error response */
+    IF_PARSE_EXEC(snmpv3_get_user((char*)request->msg_user_name, NULL, NULL, NULL, NULL));
+
+    /* msgAuthenticationParameters */
+    memset(request->msg_authentication_parameters, 0, SNMP_V3_MAX_AUTH_PARAM_LENGTH);
+    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
+    parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+    /* Remember position */
+    inbound_msgAuthenticationParameters_offset = pbuf_stream.offset;
+    LWIP_UNUSED_ARG(inbound_msgAuthenticationParameters_offset);
+    /* Read auth parameters */
+    IF_PARSE_ASSERT(tlv.value_len <= SNMP_V3_MAX_AUTH_PARAM_LENGTH);
+    IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->msg_authentication_parameters,
+        &u16_value, tlv.value_len));
+
+#if LWIP_SNMP_V3_CRYPTO
+    if (request->msg_flags & SNMP_V3_AUTH_FLAG) {
+      const u8_t zero_arr[SNMP_V3_MAX_AUTH_PARAM_LENGTH] = { 0 };
+      u8_t key[20];
+      u8_t algo;
+      u8_t hmac[LWIP_MAX(SNMP_V3_SHA_LEN, SNMP_V3_MD5_LEN)];
+      struct snmp_pbuf_stream auth_stream;
+
+      /* Rewind stream */
+      IF_PARSE_EXEC(snmp_pbuf_stream_init(&pbuf_stream, request->inbound_pbuf, 0, request->inbound_pbuf->tot_len));
+      IF_PARSE_EXEC(snmp_pbuf_stream_seek_abs(&pbuf_stream, inbound_msgAuthenticationParameters_offset));
+      /* Set auth parameters to zero for verification */
+      IF_PARSE_EXEC(snmp_asn1_enc_raw(&pbuf_stream, zero_arr, tlv.value_len));
+
+      /* Verify authentication */
+      IF_PARSE_EXEC(snmp_pbuf_stream_init(&auth_stream, request->inbound_pbuf, 0, request->inbound_pbuf->tot_len));
+
+      IF_PARSE_EXEC(snmpv3_get_user((char*)request->msg_user_name, &algo, key, NULL, NULL));
+      IF_PARSE_EXEC(snmpv3_auth(&auth_stream, request->inbound_pbuf->tot_len, key, algo, hmac));
+      /* @todo: Implement error response */
+      IF_PARSE_EXEC(memcmp(request->msg_authentication_parameters, hmac, SNMP_V3_MAX_AUTH_PARAM_LENGTH));
+    }
+#else
+    /* Ungraceful exit if we encounter cryptography and don't support it.
+     * @todo: Implement error response
+     */
+    IF_PARSE_ASSERT(!(request->msg_flags & (SNMP_V3_AUTH_FLAG | SNMP_V3_PRIV_FLAG)));
+#endif
+
+    /* msgPrivacyParameters */
+    memset(request->msg_privacy_parameters, 0, SNMP_V3_MAX_PRIV_PARAM_LENGTH);
+    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
+    parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+    IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->msg_privacy_parameters,
+        &u16_value, SNMP_V3_MAX_PRIV_PARAM_LENGTH));
+
+#if LWIP_SNMP_V3_CRYPTO
+    /* Decrypt message */
+    if (request->msg_flags & SNMP_V3_PRIV_FLAG) {
+      u8_t key[20];
+      u8_t algo;
+
+      IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+      IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
+      parent_tlv_value_len -= SNMP_ASN1_TLV_HDR_LENGTH(tlv);
+      IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+      IF_PARSE_EXEC(snmpv3_get_user((char*)request->msg_user_name, NULL, NULL, &algo, key));
+      IF_PARSE_EXEC(snmpv3_crypt(&pbuf_stream, tlv.value_len, key,
+          request->msg_privacy_parameters, request->msg_authoritative_engine_boots,
+          request->msg_authoritative_engine_time, algo, SNMP_V3_PRIV_MODE_DECRYPT));
+    }
+#endif
+
+    /* Scoped PDU
+     * Encryption context
+     */
+    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_SEQUENCE);
+    parent_tlv_value_len -= SNMP_ASN1_TLV_HDR_LENGTH(tlv);
+    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+    /* contextEngineID */
+    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
+    parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+    IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->context_engine_id,
+        &u16_value, SNMP_V3_MAX_ENGINE_ID_LENGTH));
+    request->context_engine_id_len = (u8_t)u16_value;
+
+    /* contextName */
+    IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+    IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
+    parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+    IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+    IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->context_name,
+        &u16_value, SNMP_V3_MAX_ENGINE_ID_LENGTH));
+    request->context_name_len = (u8_t)u16_value;
+  } else
+#endif
+  {
+  /* decode community */
+  IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+  IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
+  parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+  IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+  err = snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->community, &request->community_strlen, SNMP_MAX_COMMUNITY_STR_LEN);
+  if (err == ERR_MEM) {
+    /* community string does not fit in our buffer -> its too long -> its invalid */
+    request->community_strlen = 0;
+    snmp_pbuf_stream_seek(&pbuf_stream, tlv.value_len);
+  } else {
+    IF_PARSE_ASSERT(err == ERR_OK);
+  }
+  /* add zero terminator */
+  request->community[request->community_strlen] = 0;
+  }
+
+  /* decode PDU type (next container level) */
+  IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+  IF_PARSE_ASSERT(tlv.value_len <= pbuf_stream.length);
+  request->inbound_padding_len = pbuf_stream.length - tlv.value_len;
+  parent_tlv_value_len = tlv.value_len;
+
+  /* validate PDU type */
+  switch(tlv.type) {
+    case (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_GET_REQ):
+      /* GetRequest PDU */
+      snmp_stats.ingetrequests++;
+      break;
+    case (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_GET_NEXT_REQ):
+      /* GetNextRequest PDU */
+      snmp_stats.ingetnexts++;
+      break;
+    case (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_GET_BULK_REQ):
+      /* GetBulkRequest PDU */
+      if (request->version < SNMP_VERSION_2c) {
+        /* RFC2089: invalid, drop packet */
+        return ERR_ARG;
+      }
+      break;
+    case (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_SET_REQ):
+      /* SetRequest PDU */
+      snmp_stats.insetrequests++;
+      break;
+    default:
+      /* unsupported input PDU for this agent (no parse error) */
+      LWIP_DEBUGF(SNMP_DEBUG, ("Unknown/Invalid SNMP PDU type received: %d", tlv.type)); \
+      return ERR_ARG;
+      break;
+  }
+  request->request_type = tlv.type & SNMP_ASN1_DATATYPE_MASK;
+
+  /* validate community (do this after decoding PDU type because we don't want to increase 'inbadcommunitynames' for wrong frame types */
+  if (request->community_strlen == 0) {
+    /* community string was too long or really empty*/
+    snmp_stats.inbadcommunitynames++;
+    snmp_authfail_trap();
+    return ERR_ARG;
+  } else if (request->request_type == SNMP_ASN1_CONTEXT_PDU_SET_REQ) {
+    if (snmp_community_write[0] == 0) {
+      /* our write community is empty, that means all our objects are readonly */
+      request->error_status = SNMP_ERR_NOTWRITABLE;
+      request->error_index  = 1;
+    } else if (strncmp(snmp_community_write, (const char*)request->community, SNMP_MAX_COMMUNITY_STR_LEN) != 0) {
+      /* community name does not match */
+      snmp_stats.inbadcommunitynames++;
+      snmp_authfail_trap();
+      return ERR_ARG;
+    }
+  } else { 
+    if (strncmp(snmp_community, (const char*)request->community, SNMP_MAX_COMMUNITY_STR_LEN) != 0) {
+      /* community name does not match */
+      snmp_stats.inbadcommunitynames++;
+      snmp_authfail_trap();
+      return ERR_ARG;
+    }
+  }
+  
+  /* decode request ID */
+  IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+  IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER);
+  parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+  IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+  
+  IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &request->request_id));
+
+  /* decode error status / non-repeaters */
+  IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+  IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER);
+  parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+  IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+  if (request->request_type == SNMP_ASN1_CONTEXT_PDU_GET_BULK_REQ) {
+    IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &request->non_repeaters));
+    if (request->non_repeaters < 0) {
+      /* RFC 1905, 4.2.3 */
+      request->non_repeaters = 0;
+    }
+  } else {
+    /* only check valid value, don't touch 'request->error_status', maybe a response error status was already set to above; */
+    IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &s32_value));
+    IF_PARSE_ASSERT(s32_value == SNMP_ERR_NOERROR);
+  }
+
+  /* decode error index / max-repetitions */
+  IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+  IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER);
+  parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+  IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+  if (request->request_type == SNMP_ASN1_CONTEXT_PDU_GET_BULK_REQ) {
+    IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &request->max_repetitions));
+    if (request->max_repetitions < 0) {
+      /* RFC 1905, 4.2.3 */
+      request->max_repetitions = 0;
+    }
+  } else {
+    IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &request->error_index));
+    IF_PARSE_ASSERT(s32_value == 0);
+  }
+
+  /* decode varbind-list type (next container level) */
+  IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+  IF_PARSE_ASSERT((tlv.type == SNMP_ASN1_TYPE_SEQUENCE) && (tlv.value_len <= pbuf_stream.length));
+  
+  request->inbound_varbind_offset = pbuf_stream.offset;
+  request->inbound_varbind_len    = pbuf_stream.length - request->inbound_padding_len;
+  snmp_vb_enumerator_init(&(request->inbound_varbind_enumerator), request->inbound_pbuf, request->inbound_varbind_offset, request->inbound_varbind_len);
+
+  return ERR_OK;
+}
+
+#define OF_BUILD_EXEC(code) BUILD_EXEC(code, ERR_ARG)
+
+static err_t
+snmp_prepare_outbound_frame(struct snmp_request *request)
+{
+  struct snmp_asn1_tlv tlv;
+  struct snmp_pbuf_stream* pbuf_stream = &(request->outbound_pbuf_stream);
+
+  /* try allocating pbuf(s) for maximum response size */
+  request->outbound_pbuf = pbuf_alloc(PBUF_TRANSPORT, 1472, PBUF_RAM);
+  if (request->outbound_pbuf == NULL) {
+    return ERR_MEM;
+  }
+
+  snmp_pbuf_stream_init(pbuf_stream, request->outbound_pbuf, 0, request->outbound_pbuf->tot_len);
+
+  /* 'Message' sequence */
+  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 3, 0);
+  OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+
+  /* version */
+  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
+  snmp_asn1_enc_s32t_cnt(request->version, &tlv.value_len);
+  OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+  OF_BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, request->version) );
+
+#if LWIP_SNMP_V3
+  if (request->version < SNMP_VERSION_3) {
+#endif
+  /* community */
+  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, request->community_strlen);
+  OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+  OF_BUILD_EXEC( snmp_asn1_enc_raw(pbuf_stream, request->community, request->community_strlen) );
+#if LWIP_SNMP_V3
+  } else {
+    const char* id;
+
+    /* globalData */
+    request->outbound_msg_global_data_offset = pbuf_stream->offset;
+    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 1, 0);
+    OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+
+    /* msgID */
+    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 1);
+    snmp_asn1_enc_s32t_cnt(request->msg_id, &tlv.value_len);
+    OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+    OF_BUILD_EXEC(snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, request->msg_id));
+
+    /* msgMaxSize */
+    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 1);
+    snmp_asn1_enc_s32t_cnt(request->msg_max_size, &tlv.value_len);
+    OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+    OF_BUILD_EXEC(snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, request->msg_max_size));
+
+    /* msgFlags */
+    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, 1);
+    OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+    OF_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, &request->msg_flags, 1));
+
+    /* msgSecurityModel */
+    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 1);
+    snmp_asn1_enc_s32t_cnt(request->msg_security_model, &tlv.value_len);
+    OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+    OF_BUILD_EXEC(snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, request->msg_security_model));
+
+    /* end of msgGlobalData */
+    request->outbound_msg_global_data_end = pbuf_stream->offset;
+
+    /* msgSecurityParameters */
+    request->outbound_msg_security_parameters_str_offset = pbuf_stream->offset;
+    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 1, 0);
+    OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+
+    request->outbound_msg_security_parameters_seq_offset = pbuf_stream->offset;
+    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 1, 0);
+    OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+
+    /* msgAuthoritativeEngineID */
+    snmpv3_get_engine_id(&id, &request->msg_authoritative_engine_id_len);
+    MEMCPY(request->msg_authoritative_engine_id, id, request->msg_authoritative_engine_id_len);
+    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, request->msg_authoritative_engine_id_len);
+    OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+    OF_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, request->msg_authoritative_engine_id, request->msg_authoritative_engine_id_len));
+
+    request->msg_authoritative_engine_time = snmpv3_get_engine_time();
+    request->msg_authoritative_engine_boots = snmpv3_get_engine_boots();
+
+    /* msgAuthoritativeEngineBoots */
+    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
+    snmp_asn1_enc_s32t_cnt(request->msg_authoritative_engine_boots, &tlv.value_len);
+    OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+    OF_BUILD_EXEC(snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, request->msg_authoritative_engine_boots));
+
+    /* msgAuthoritativeEngineTime */
+    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
+    snmp_asn1_enc_s32t_cnt(request->msg_authoritative_engine_time, &tlv.value_len);
+    OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+    OF_BUILD_EXEC(snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, request->msg_authoritative_engine_time));
+
+    /* msgUserName */
+    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, request->msg_user_name_len);
+    OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+    OF_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, request->msg_user_name, request->msg_user_name_len));
+
+#if LWIP_SNMP_V3_CRYPTO
+    /* msgAuthenticationParameters */
+    if (request->msg_flags & SNMP_V3_AUTH_FLAG) {
+      memset(request->msg_authentication_parameters, 0, SNMP_V3_MAX_AUTH_PARAM_LENGTH);
+      request->outbound_msg_authentication_parameters_offset = pbuf_stream->offset;
+      SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 1, SNMP_V3_MAX_AUTH_PARAM_LENGTH);
+      OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+      OF_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, request->msg_authentication_parameters, SNMP_V3_MAX_AUTH_PARAM_LENGTH));
+    } else
+#endif
+    {
+      SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, 0);
+      OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+    }
+
+#if LWIP_SNMP_V3_CRYPTO
+    /* msgPrivacyParameters */
+    if (request->msg_flags & SNMP_V3_PRIV_FLAG) {
+      snmpv3_build_priv_param(request->msg_privacy_parameters);
+
+      SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, SNMP_V3_MAX_PRIV_PARAM_LENGTH);
+      OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+      OF_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, request->msg_privacy_parameters, SNMP_V3_MAX_PRIV_PARAM_LENGTH));
+    } else
+#endif
+    {
+      SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, 0);
+      OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+    }
+
+    /* End of msgSecurityParameters, so we can calculate the length of this sequence later */
+    request->outbound_msg_security_parameters_end = pbuf_stream->offset;
+
+#if LWIP_SNMP_V3_CRYPTO
+    /* For encryption we have to encapsulate the payload in an octet string */
+    if (request->msg_flags & SNMP_V3_PRIV_FLAG) {
+      request->outbound_scoped_pdu_string_offset = pbuf_stream->offset;
+      SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 3, 0);
+      OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+    }
+#endif
+    /* Scoped PDU
+     * Encryption context
+     */
+    request->outbound_scoped_pdu_seq_offset = pbuf_stream->offset;
+    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 3, 0);
+    OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+
+    /* contextEngineID */
+    snmpv3_get_engine_id(&id, &request->context_engine_id_len);
+    MEMCPY(request->context_engine_id, id, request->context_engine_id_len);
+    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, request->context_engine_id_len);
+    OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+    OF_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, request->context_engine_id, request->context_engine_id_len));
+
+    /* contextName */
+    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, request->context_name_len);
+    OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+    OF_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, request->context_name, request->context_name_len));
+  }
+#endif
+
+  /* 'PDU' sequence */
+  request->outbound_pdu_offset = pbuf_stream->offset;
+  SNMP_ASN1_SET_TLV_PARAMS(tlv, (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_GET_RESP), 3, 0);
+  OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+
+  /* request ID */
+  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
+  snmp_asn1_enc_s32t_cnt(request->request_id, &tlv.value_len);
+  OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+  OF_BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, request->request_id) );
+
+  /* error status */
+  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 1);
+  OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+  request->outbound_error_status_offset = pbuf_stream->offset;
+  OF_BUILD_EXEC( snmp_pbuf_stream_write(pbuf_stream, 0) );
+
+  /* error index */
+  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 1);
+  OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+  request->outbound_error_index_offset = pbuf_stream->offset;
+  OF_BUILD_EXEC( snmp_pbuf_stream_write(pbuf_stream, 0) );
+
+  /* 'VarBindList' sequence */
+  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 3, 0);
+  OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+
+  request->outbound_varbind_offset = pbuf_stream->offset;
+
+  return ERR_OK;
+}
+
+/** Calculate the length of a varbind list */
+err_t
+snmp_varbind_length(struct snmp_varbind *varbind, struct snmp_varbind_len *len)
+{
+  /* calculate required lengths */
+  snmp_asn1_enc_oid_cnt(varbind->oid.id, varbind->oid.len, &len->oid_value_len);
+  snmp_asn1_enc_length_cnt(len->oid_value_len, &len->oid_len_len);
+
+  if (varbind->value_len == 0) {
+    len->value_value_len = 0;
+  } else if (varbind->value_len & SNMP_GET_VALUE_RAW_DATA) {
+    len->value_value_len = varbind->value_len & (~SNMP_GET_VALUE_RAW_DATA);
+  } else {
+    switch (varbind->type) {
+      case SNMP_ASN1_TYPE_INTEGER:
+        if (varbind->value_len != sizeof (s32_t)) {
+          return ERR_VAL;
+        }
+        snmp_asn1_enc_s32t_cnt(*((s32_t*) varbind->value), &len->value_value_len);
+        break;
+      case SNMP_ASN1_TYPE_COUNTER:
+      case SNMP_ASN1_TYPE_GAUGE:
+      case SNMP_ASN1_TYPE_TIMETICKS:
+        if (varbind->value_len != sizeof (u32_t)) {
+          return ERR_VAL;
+        }
+        snmp_asn1_enc_u32t_cnt(*((u32_t*) varbind->value), &len->value_value_len);
+        break;
+      case SNMP_ASN1_TYPE_OCTET_STRING:
+      case SNMP_ASN1_TYPE_IPADDR:
+      case SNMP_ASN1_TYPE_OPAQUE:
+        len->value_value_len = varbind->value_len;
+        break;
+      case SNMP_ASN1_TYPE_NULL:
+        if (varbind->value_len != 0) {
+          return ERR_VAL;
+        }
+        len->value_value_len = 0;
+        break;
+      case SNMP_ASN1_TYPE_OBJECT_ID:
+        if ((varbind->value_len & 0x03) != 0) {
+          return ERR_VAL;
+        }
+        snmp_asn1_enc_oid_cnt((u32_t*) varbind->value, varbind->value_len >> 2, &len->value_value_len);
+        break;
+      case SNMP_ASN1_TYPE_COUNTER64:
+        if (varbind->value_len != (2 * sizeof (u32_t))) {
+          return ERR_VAL;
+        }
+        snmp_asn1_enc_u64t_cnt((u32_t*) varbind->value, &len->value_value_len);
+        break;
+      default:
+        /* unsupported type */
+        return ERR_VAL;
+    }
+  }
+  snmp_asn1_enc_length_cnt(len->value_value_len, &len->value_len_len);
+
+  len->vb_value_len = 1 + len->oid_len_len + len->oid_value_len + 1 + len->value_len_len + len->value_value_len;
+  snmp_asn1_enc_length_cnt(len->vb_value_len, &len->vb_len_len);
+
+  return ERR_OK;
+}
+
+#define OVB_BUILD_EXEC(code) BUILD_EXEC(code, ERR_ARG)
+
+err_t
+snmp_append_outbound_varbind(struct snmp_pbuf_stream *pbuf_stream, struct snmp_varbind* varbind)
+{
+  struct snmp_asn1_tlv tlv;
+  struct snmp_varbind_len len;
+  err_t err;
+
+  err = snmp_varbind_length(varbind, &len);
+
+  if (err != ERR_OK) {
+    return err;
+  }
+
+  /* check length already before adding first data because in case of GetBulk,
+   *  data added so far is returned and therefore no partial data shall be added
+   */
+  if ((1 + len.vb_len_len + len.vb_value_len) > pbuf_stream->length) {
+    return ERR_BUF;
+  }
+
+  /* 'VarBind' sequence */
+  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, len.vb_len_len, len.vb_value_len);
+  OVB_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+
+  /* VarBind OID */
+  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OBJECT_ID, len.oid_len_len, len.oid_value_len);
+  OVB_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+  OVB_BUILD_EXEC(snmp_asn1_enc_oid(pbuf_stream, varbind->oid.id, varbind->oid.len));
+
+  /* VarBind value */
+  SNMP_ASN1_SET_TLV_PARAMS(tlv, varbind->type, len.value_len_len, len.value_value_len);
+  OVB_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+
+  if (len.value_value_len > 0) {
+    if (varbind->value_len & SNMP_GET_VALUE_RAW_DATA) {
+      OVB_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, (u8_t*) varbind->value, len.value_value_len));
+    } else {
+      switch (varbind->type) {
+        case SNMP_ASN1_TYPE_INTEGER:
+          OVB_BUILD_EXEC(snmp_asn1_enc_s32t(pbuf_stream, len.value_value_len, *((s32_t*) varbind->value)));
+          break;
+        case SNMP_ASN1_TYPE_COUNTER:
+        case SNMP_ASN1_TYPE_GAUGE:
+        case SNMP_ASN1_TYPE_TIMETICKS:
+          OVB_BUILD_EXEC(snmp_asn1_enc_u32t(pbuf_stream, len.value_value_len, *((u32_t*) varbind->value)));
+          break;
+        case SNMP_ASN1_TYPE_OCTET_STRING:
+        case SNMP_ASN1_TYPE_IPADDR:
+        case SNMP_ASN1_TYPE_OPAQUE:
+          OVB_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, (u8_t*) varbind->value, len.value_value_len));
+          len.value_value_len = varbind->value_len;
+          break;
+        case SNMP_ASN1_TYPE_OBJECT_ID:
+          OVB_BUILD_EXEC(snmp_asn1_enc_oid(pbuf_stream, (u32_t*) varbind->value, varbind->value_len / sizeof (u32_t)));
+          break;
+        case SNMP_ASN1_TYPE_COUNTER64:
+          OVB_BUILD_EXEC(snmp_asn1_enc_u64t(pbuf_stream, len.value_value_len, (u32_t*) varbind->value));
+          break;
+        default:
+          LWIP_ASSERT("Unknown variable type", 0);
+          break;
+      }
+    }
+  }
+
+  return ERR_OK;
+}
+
+static err_t
+snmp_complete_outbound_frame(struct snmp_request *request)
+{
+  struct snmp_asn1_tlv tlv;
+  u16_t frame_size;
+  u8_t outbound_padding = 0;
+
+  if (request->version == SNMP_VERSION_1) {
+    if (request->error_status != SNMP_ERR_NOERROR) {
+      /* map v2c error codes to v1 compliant error code (according to RFC 2089) */
+      switch (request->error_status) {
+        /* mapping of implementation specific "virtual" error codes 
+         * (during processing of frame we already stored them in error_status field, 
+         * so no need to check all varbinds here for those exceptions as suggested by RFC) */
+        case SNMP_ERR_NOSUCHINSTANCE:
+        case SNMP_ERR_NOSUCHOBJECT:
+        case SNMP_ERR_ENDOFMIBVIEW:
+          request->error_status = SNMP_ERR_NOSUCHNAME;
+          break;
+        /* mapping according to RFC */
+        case SNMP_ERR_WRONGVALUE:
+        case SNMP_ERR_WRONGENCODING:
+        case SNMP_ERR_WRONGTYPE:
+        case SNMP_ERR_WRONGLENGTH:
+        case SNMP_ERR_INCONSISTENTVALUE:
+          request->error_status = SNMP_ERR_BADVALUE;
+          break;
+        case SNMP_ERR_NOACCESS:
+        case SNMP_ERR_NOTWRITABLE:
+        case SNMP_ERR_NOCREATION:
+        case SNMP_ERR_INCONSISTENTNAME:
+        case SNMP_ERR_AUTHORIZATIONERROR:
+          request->error_status = SNMP_ERR_NOSUCHNAME;
+          break;
+        case SNMP_ERR_RESOURCEUNAVAILABLE:
+        case SNMP_ERR_COMMITFAILED:
+        case SNMP_ERR_UNDOFAILED:
+        default:
+          request->error_status = SNMP_ERR_GENERROR;
+          break;
+       }
+    }
+  } else {
+    if (request->request_type == SNMP_ASN1_CONTEXT_PDU_SET_REQ) {
+      /* map error codes to according to RFC 1905 (4.2.5.  The SetRequest-PDU) return 'NotWritable' for unknown OIDs) */
+      switch (request->error_status) {
+        case SNMP_ERR_NOSUCHINSTANCE:
+        case SNMP_ERR_NOSUCHOBJECT:
+        case SNMP_ERR_ENDOFMIBVIEW:
+          request->error_status = SNMP_ERR_NOTWRITABLE;
+          break;
+        default:
+          break;
+      }
+    }
+
+    if (request->error_status >= SNMP_VARBIND_EXCEPTION_OFFSET) {
+      /* should never occur because v2 frames store exceptions directly inside varbinds and not as frame error_status */
+      LWIP_DEBUGF(SNMP_DEBUG, ("snmp_complete_outbound_frame() > Found v2 request with varbind exception code stored as error status!\n"));
+      return ERR_ARG;
+    }
+  }
+
+  if ((request->error_status != SNMP_ERR_NOERROR) || (request->request_type == SNMP_ASN1_CONTEXT_PDU_SET_REQ)) {
+    /* all inbound vars are returned in response without any modification for error responses and successful set requests*/
+    struct snmp_pbuf_stream inbound_stream;
+    OF_BUILD_EXEC( snmp_pbuf_stream_init(&inbound_stream, request->inbound_pbuf, request->inbound_varbind_offset, request->inbound_varbind_len) );
+    OF_BUILD_EXEC( snmp_pbuf_stream_init(&(request->outbound_pbuf_stream), request->outbound_pbuf, request->outbound_varbind_offset, request->outbound_pbuf->tot_len - request->outbound_varbind_offset) );
+    snmp_pbuf_stream_writeto(&inbound_stream, &(request->outbound_pbuf_stream), 0);
+  }
+
+  frame_size = request->outbound_pbuf_stream.offset;
+
+#if LWIP_SNMP_V3 && LWIP_SNMP_V3_CRYPTO
+  /* Calculate padding for encryption */
+  if (request->version == SNMP_VERSION_3 && (request->msg_flags & SNMP_V3_PRIV_FLAG)) {
+    u8_t i;
+    outbound_padding = (8 - (u8_t)((frame_size - request->outbound_scoped_pdu_seq_offset) & 0x07)) & 0x07;
+    for (i = 0; i < outbound_padding; i++) {
+      snmp_pbuf_stream_write(&request->outbound_pbuf_stream, 0);
+    }
+  }
+#endif
+
+  /* complete missing length in 'Message' sequence ; 'Message' tlv is located at the beginning (offset 0) */
+  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 3, frame_size + outbound_padding - 1 - 3); /* - type - length_len(fixed, see snmp_prepare_outbound_frame()) */
+  OF_BUILD_EXEC( snmp_pbuf_stream_init(&(request->outbound_pbuf_stream), request->outbound_pbuf, 0, request->outbound_pbuf->tot_len) );
+  OF_BUILD_EXEC( snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv) );
+
+#if LWIP_SNMP_V3
+  if (request->version == SNMP_VERSION_3) {
+    /* complete missing length in 'globalData' sequence */
+    /* - type - length_len(fixed, see snmp_prepare_outbound_frame()) */
+    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 1, request->outbound_msg_global_data_end
+        - request->outbound_msg_global_data_offset - 1 - 1);
+    OF_BUILD_EXEC(snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_msg_global_data_offset));
+    OF_BUILD_EXEC(snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv));
+
+    /* complete missing length in 'msgSecurityParameters' sequence */
+    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 1, request->outbound_msg_security_parameters_end
+        - request->outbound_msg_security_parameters_str_offset - 1 - 1);
+    OF_BUILD_EXEC(snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_msg_security_parameters_str_offset));
+    OF_BUILD_EXEC(snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv));
+
+    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 1, request->outbound_msg_security_parameters_end
+        - request->outbound_msg_security_parameters_seq_offset - 1 - 1);
+    OF_BUILD_EXEC(snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_msg_security_parameters_seq_offset));
+    OF_BUILD_EXEC(snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv));
+
+    /* complete missing length in scoped PDU sequence */
+    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 3, frame_size - request->outbound_scoped_pdu_seq_offset - 1 - 3);
+    OF_BUILD_EXEC(snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_scoped_pdu_seq_offset));
+    OF_BUILD_EXEC(snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv));
+  }
+#endif
+
+  /* complete missing length in 'PDU' sequence */
+  SNMP_ASN1_SET_TLV_PARAMS(tlv, (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_GET_RESP), 3,
+      frame_size - request->outbound_pdu_offset - 1 - 3); /* - type - length_len(fixed, see snmp_prepare_outbound_frame()) */
+  OF_BUILD_EXEC( snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_pdu_offset) );
+  OF_BUILD_EXEC( snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv) );
+
+  /* process and encode final error status */
+  if (request->error_status != 0) {
+    u16_t len;
+    snmp_asn1_enc_s32t_cnt(request->error_status, &len);
+    if (len != 1) {
+      /* error, we only reserved one byte for it */
+      return ERR_ARG;
+    }
+    OF_BUILD_EXEC( snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_error_status_offset) );
+    OF_BUILD_EXEC( snmp_asn1_enc_s32t(&(request->outbound_pbuf_stream), len, request->error_status) );
+
+    /* for compatibility to v1, log statistics; in v2 (RFC 1907) these statistics are obsoleted */
+    switch (request->error_status) {
+      case SNMP_ERR_TOOBIG:
+        snmp_stats.outtoobigs++;
+        break;
+      case SNMP_ERR_NOSUCHNAME:
+        snmp_stats.outnosuchnames++;
+        break;
+      case SNMP_ERR_BADVALUE:
+        snmp_stats.outbadvalues++;
+        break;
+      case SNMP_ERR_GENERROR:
+      default:
+        snmp_stats.outgenerrs++;
+        break;
+    }
+
+    if (request->error_status == SNMP_ERR_TOOBIG) {
+      request->error_index = 0; /* defined by RFC 1157 */
+    } else if (request->error_index == 0) {
+      /* set index to varbind where error occured (if not already set before, e.g. during GetBulk processing) */
+      request->error_index = request->inbound_varbind_enumerator.varbind_count;
+    }
+  } else {
+    if (request->request_type == SNMP_ASN1_CONTEXT_PDU_SET_REQ) {
+      snmp_stats.intotalsetvars += request->inbound_varbind_enumerator.varbind_count;
+    } else {
+      snmp_stats.intotalreqvars += request->inbound_varbind_enumerator.varbind_count;
+    }
+  }
+
+  /* encode final error index*/
+  if (request->error_index != 0) {
+    u16_t len;
+    snmp_asn1_enc_s32t_cnt(request->error_index, &len);
+    if (len != 1) {
+      /* error, we only reserved one byte for it */
+      return ERR_VAL;
+    }
+    OF_BUILD_EXEC( snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_error_index_offset) );
+    OF_BUILD_EXEC( snmp_asn1_enc_s32t(&(request->outbound_pbuf_stream), len, request->error_index) );
+  }
+
+  /* complete missing length in 'VarBindList' sequence ; 'VarBindList' tlv is located directly before varbind offset */
+  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 3, frame_size - request->outbound_varbind_offset);
+  OF_BUILD_EXEC( snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_varbind_offset - 1 - 3) ); /* - type - length_len(fixed, see snmp_prepare_outbound_frame()) */
+  OF_BUILD_EXEC( snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv) );
+
+  /* Authenticate response */
+#if LWIP_SNMP_V3 && LWIP_SNMP_V3_CRYPTO
+  /* Encrypt response */
+  if (request->version == SNMP_VERSION_3 && (request->msg_flags & SNMP_V3_PRIV_FLAG)) {
+    u8_t key[20];
+    u8_t algo;
+
+    /* complete missing length in PDU sequence */
+    OF_BUILD_EXEC(snmp_pbuf_stream_init(&request->outbound_pbuf_stream, request->outbound_pbuf, 0, request->outbound_pbuf->tot_len));
+    OF_BUILD_EXEC(snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_scoped_pdu_string_offset));
+    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 3, frame_size + outbound_padding
+        - request->outbound_scoped_pdu_string_offset - 1 - 3);
+    OF_BUILD_EXEC(snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv));
+
+    OF_BUILD_EXEC(snmpv3_get_user((char*)request->msg_user_name, NULL, NULL, &algo, key));
+
+    OF_BUILD_EXEC(snmpv3_crypt(&request->outbound_pbuf_stream, tlv.value_len, key,
+        request->msg_privacy_parameters, request->msg_authoritative_engine_boots,
+        request->msg_authoritative_engine_time, algo, SNMP_V3_PRIV_MODE_ENCRYPT));
+  }
+
+  if (request->version == SNMP_VERSION_3 && (request->msg_flags & SNMP_V3_AUTH_FLAG)) {
+    u8_t key[20];
+    u8_t algo;
+    u8_t hmac[20];
+
+    OF_BUILD_EXEC(snmpv3_get_user((char*)request->msg_user_name, &algo, key, NULL, NULL));
+    OF_BUILD_EXEC(snmp_pbuf_stream_init(&(request->outbound_pbuf_stream),
+        request->outbound_pbuf, 0, request->outbound_pbuf->tot_len));
+    OF_BUILD_EXEC(snmpv3_auth(&request->outbound_pbuf_stream, frame_size + outbound_padding, key, algo, hmac));
+
+    MEMCPY(request->msg_authentication_parameters, hmac, SNMP_V3_MAX_AUTH_PARAM_LENGTH);
+    OF_BUILD_EXEC(snmp_pbuf_stream_init(&request->outbound_pbuf_stream,
+                  request->outbound_pbuf, 0, request->outbound_pbuf->tot_len));
+    OF_BUILD_EXEC(snmp_pbuf_stream_seek_abs(&request->outbound_pbuf_stream,
+                  request->outbound_msg_authentication_parameters_offset));
+
+    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 1, SNMP_V3_MAX_AUTH_PARAM_LENGTH);
+    OF_BUILD_EXEC(snmp_ans1_enc_tlv(&request->outbound_pbuf_stream, &tlv));
+    OF_BUILD_EXEC(snmp_asn1_enc_raw(&request->outbound_pbuf_stream,
+                  request->msg_authentication_parameters, SNMP_V3_MAX_AUTH_PARAM_LENGTH));
+  }
+#endif
+
+  pbuf_realloc(request->outbound_pbuf, frame_size + outbound_padding);
+
+  snmp_stats.outgetresponses++;
+  snmp_stats.outpkts++;
+
+  return ERR_OK;
+}
+
+static void 
+snmp_execute_write_callbacks(struct snmp_request *request)
+{
+  struct snmp_varbind_enumerator inbound_varbind_enumerator;
+  struct snmp_varbind vb;
+
+  snmp_vb_enumerator_init(&inbound_varbind_enumerator, request->inbound_pbuf, request->inbound_varbind_offset, request->inbound_varbind_len);
+  vb.value = NULL; /* do NOT decode value (we enumerate outbound buffer here, so all varbinds have values assigned, which we don't need here) */
+
+  while (snmp_vb_enumerator_get_next(&inbound_varbind_enumerator, &vb) == SNMP_VB_ENUMERATOR_ERR_OK) {
+    snmp_write_callback(vb.oid.id, vb.oid.len, snmp_write_callback_arg);
+  }
+}
+
+
+/* ----------------------------------------------------------------------- */
+/* VarBind enumerator methods */
+/* ----------------------------------------------------------------------- */
+
+void
+snmp_vb_enumerator_init(struct snmp_varbind_enumerator* enumerator, struct pbuf* p, u16_t offset, u16_t length)
+{
+  snmp_pbuf_stream_init(&(enumerator->pbuf_stream), p, offset, length);
+  enumerator->varbind_count = 0;
+}
+
+#define VB_PARSE_EXEC(code)   PARSE_EXEC(code, SNMP_VB_ENUMERATOR_ERR_ASN1ERROR)
+#define VB_PARSE_ASSERT(code) PARSE_ASSERT(code, SNMP_VB_ENUMERATOR_ERR_ASN1ERROR)
+
+snmp_vb_enumerator_err_t
+snmp_vb_enumerator_get_next(struct snmp_varbind_enumerator* enumerator, struct snmp_varbind* varbind)
+{
+  struct snmp_asn1_tlv tlv;
+  u16_t  varbind_len;
+  err_t  err;
+  
+  if (enumerator->pbuf_stream.length == 0)
+  {
+    return SNMP_VB_ENUMERATOR_ERR_EOVB;
+  }
+  enumerator->varbind_count++;
+
+  /* decode varbind itself (parent container of a varbind) */
+  VB_PARSE_EXEC(snmp_asn1_dec_tlv(&(enumerator->pbuf_stream), &tlv));
+  VB_PARSE_ASSERT((tlv.type == SNMP_ASN1_TYPE_SEQUENCE) && (tlv.value_len <= enumerator->pbuf_stream.length));
+  varbind_len = tlv.value_len;
+
+  /* decode varbind name (object id) */
+  VB_PARSE_EXEC(snmp_asn1_dec_tlv(&(enumerator->pbuf_stream), &tlv));
+  VB_PARSE_ASSERT((tlv.type == SNMP_ASN1_TYPE_OBJECT_ID) && (SNMP_ASN1_TLV_LENGTH(tlv) < varbind_len) && (tlv.value_len < enumerator->pbuf_stream.length));
+   
+  VB_PARSE_EXEC(snmp_asn1_dec_oid(&(enumerator->pbuf_stream), tlv.value_len, varbind->oid.id, &(varbind->oid.len), SNMP_MAX_OBJ_ID_LEN));
+  varbind_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+
+  /* decode varbind value (object id) */
+  VB_PARSE_EXEC(snmp_asn1_dec_tlv(&(enumerator->pbuf_stream), &tlv));
+  VB_PARSE_ASSERT((SNMP_ASN1_TLV_LENGTH(tlv) == varbind_len) && (tlv.value_len <= enumerator->pbuf_stream.length));
+  varbind->type = tlv.type;
+
+  /* shall the value be decoded ? */
+  if (varbind->value != NULL) {
+    switch (varbind->type) {
+      case SNMP_ASN1_TYPE_INTEGER:
+        VB_PARSE_EXEC(snmp_asn1_dec_s32t(&(enumerator->pbuf_stream), tlv.value_len, (s32_t*)varbind->value));
+        varbind->value_len = sizeof(s32_t*);
+        break;
+      case SNMP_ASN1_TYPE_COUNTER:
+      case SNMP_ASN1_TYPE_GAUGE:
+      case SNMP_ASN1_TYPE_TIMETICKS:
+        VB_PARSE_EXEC(snmp_asn1_dec_u32t(&(enumerator->pbuf_stream), tlv.value_len, (u32_t*)varbind->value));
+        varbind->value_len = sizeof(u32_t*);
+        break;
+      case SNMP_ASN1_TYPE_OCTET_STRING:
+      case SNMP_ASN1_TYPE_OPAQUE:
+        err = snmp_asn1_dec_raw(&(enumerator->pbuf_stream), tlv.value_len, (u8_t*)varbind->value, &varbind->value_len, SNMP_MAX_VALUE_SIZE);
+        if (err == ERR_MEM) {
+          return SNMP_VB_ENUMERATOR_ERR_INVALIDLENGTH;
+        }
+        VB_PARSE_ASSERT(err == ERR_OK);
+        break;
+      case SNMP_ASN1_TYPE_NULL:
+        varbind->value_len = 0;
+        break;
+      case SNMP_ASN1_TYPE_OBJECT_ID:
+        /* misuse tlv.length_len as OID_length transporter */
+        err = snmp_asn1_dec_oid(&(enumerator->pbuf_stream), tlv.value_len, (u32_t*)varbind->value, &tlv.length_len, SNMP_MAX_OBJ_ID_LEN);
+        if (err == ERR_MEM) {
+          return SNMP_VB_ENUMERATOR_ERR_INVALIDLENGTH;
+        }
+        VB_PARSE_ASSERT(err == ERR_OK);
+        varbind->value_len = tlv.length_len * sizeof(u32_t);
+        break;
+      case SNMP_ASN1_TYPE_IPADDR:
+        if (tlv.value_len == 4) {
+          /* must be exactly 4 octets! */
+          VB_PARSE_EXEC(snmp_asn1_dec_raw(&(enumerator->pbuf_stream), tlv.value_len, (u8_t*)varbind->value, &varbind->value_len, SNMP_MAX_VALUE_SIZE));
+        } else {
+          VB_PARSE_ASSERT(0);
+        }
+        break;
+      case SNMP_ASN1_TYPE_COUNTER64:
+        VB_PARSE_EXEC(snmp_asn1_dec_u64t(&(enumerator->pbuf_stream), tlv.value_len, (u32_t*)varbind->value));
+        varbind->value_len = 2 * sizeof(u32_t*);
+        break;
+      default:
+        VB_PARSE_ASSERT(0);
+        break;
+    }
+  } else {
+    snmp_pbuf_stream_seek(&(enumerator->pbuf_stream), tlv.value_len);
+    varbind->value_len = tlv.value_len;
+  }
+
+  return SNMP_VB_ENUMERATOR_ERR_OK;
+}
+
+#endif /* LWIP_SNMP */

+ 194 - 185
libs/thirdparty/lwip_2.1.2/src/apps/snmp/snmp_msg.h → libs/thirdparty/LwIP/src/apps/snmp/snmp_msg.h

@@ -1,185 +1,194 @@
-/**
- * @file
- * SNMP Agent message handling structures (internal API, do not use in client code).
- */
-
-/*
- * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
- * Copyright (c) 2016 Elias Oenal.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * Author: Christiaan Simons <christiaan.simons@axon.tv>
- *         Martin Hentschel <info@cl-soft.de>
- *         Elias Oenal <lwip@eliasoenal.com>
- */
-
-#ifndef LWIP_HDR_APPS_SNMP_MSG_H
-#define LWIP_HDR_APPS_SNMP_MSG_H
-
-#include "lwip/apps/snmp_opts.h"
-
-#if LWIP_SNMP
-
-#include "lwip/apps/snmp.h"
-#include "lwip/apps/snmp_core.h"
-#include "snmp_pbuf_stream.h"
-#include "lwip/ip_addr.h"
-#include "lwip/err.h"
-
-#if LWIP_SNMP_V3
-#include "snmpv3_priv.h"
-#endif
-
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* version defines used in PDU */
-#define SNMP_VERSION_1  0
-#define SNMP_VERSION_2c 1
-#define SNMP_VERSION_3  3
-
-struct snmp_varbind_enumerator {
-  struct snmp_pbuf_stream pbuf_stream;
-  u16_t varbind_count;
-};
-
-typedef enum {
-  SNMP_VB_ENUMERATOR_ERR_OK            = 0,
-  SNMP_VB_ENUMERATOR_ERR_EOVB          = 1,
-  SNMP_VB_ENUMERATOR_ERR_ASN1ERROR     = 2,
-  SNMP_VB_ENUMERATOR_ERR_INVALIDLENGTH = 3
-} snmp_vb_enumerator_err_t;
-
-void snmp_vb_enumerator_init(struct snmp_varbind_enumerator *enumerator, struct pbuf *p, u16_t offset, u16_t length);
-snmp_vb_enumerator_err_t snmp_vb_enumerator_get_next(struct snmp_varbind_enumerator *enumerator, struct snmp_varbind *varbind);
-
-struct snmp_request {
-  /* Communication handle */
-  void *handle;
-  /* source IP address */
-  const ip_addr_t *source_ip;
-  /* source UDP port */
-  u16_t source_port;
-  /* incoming snmp version */
-  u8_t version;
-  /* community name (zero terminated) */
-  u8_t community[SNMP_MAX_COMMUNITY_STR_LEN + 1];
-  /* community string length (exclusive zero term) */
-  u16_t community_strlen;
-  /* request type */
-  u8_t request_type;
-  /* request ID */
-  s32_t request_id;
-  /* error status */
-  s32_t error_status;
-  /* error index */
-  s32_t error_index;
-  /* non-repeaters (getBulkRequest (SNMPv2c)) */
-  s32_t non_repeaters;
-  /* max-repetitions (getBulkRequest (SNMPv2c)) */
-  s32_t max_repetitions;
-
-  /* Usually response-pdu (2). When snmpv3 errors are detected report-pdu(8) */
-  u8_t request_out_type;
-
-#if LWIP_SNMP_V3
-  s32_t msg_id;
-  s32_t msg_max_size;
-  u8_t  msg_flags;
-  s32_t msg_security_model;
-  u8_t  msg_authoritative_engine_id[SNMP_V3_MAX_ENGINE_ID_LENGTH];
-  u8_t  msg_authoritative_engine_id_len;
-  s32_t msg_authoritative_engine_boots;
-  s32_t msg_authoritative_engine_time;
-  u8_t  msg_user_name[SNMP_V3_MAX_USER_LENGTH];
-  u8_t  msg_user_name_len;
-  u8_t  msg_authentication_parameters[SNMP_V3_MAX_AUTH_PARAM_LENGTH];
-  u8_t  msg_authentication_parameters_len;
-  u8_t  msg_privacy_parameters[SNMP_V3_MAX_PRIV_PARAM_LENGTH];
-  u8_t  msg_privacy_parameters_len;
-  u8_t  context_engine_id[SNMP_V3_MAX_ENGINE_ID_LENGTH];
-  u8_t  context_engine_id_len;
-  u8_t  context_name[SNMP_V3_MAX_ENGINE_ID_LENGTH];
-  u8_t  context_name_len;
-#endif
-
-  struct pbuf *inbound_pbuf;
-  struct snmp_varbind_enumerator inbound_varbind_enumerator;
-  u16_t inbound_varbind_offset;
-  u16_t inbound_varbind_len;
-  u16_t inbound_padding_len;
-
-  struct pbuf *outbound_pbuf;
-  struct snmp_pbuf_stream outbound_pbuf_stream;
-  u16_t outbound_pdu_offset;
-  u16_t outbound_error_status_offset;
-  u16_t outbound_error_index_offset;
-  u16_t outbound_varbind_offset;
-#if LWIP_SNMP_V3
-  u16_t outbound_msg_global_data_offset;
-  u16_t outbound_msg_global_data_end;
-  u16_t outbound_msg_security_parameters_str_offset;
-  u16_t outbound_msg_security_parameters_seq_offset;
-  u16_t outbound_msg_security_parameters_end;
-  u16_t outbound_msg_authentication_parameters_offset;
-  u16_t outbound_scoped_pdu_seq_offset;
-  u16_t outbound_scoped_pdu_string_offset;
-#endif
-
-  u8_t value_buffer[SNMP_MAX_VALUE_SIZE];
-};
-
-/** A helper struct keeping length information about varbinds */
-struct snmp_varbind_len {
-  u8_t  vb_len_len;
-  u16_t vb_value_len;
-  u8_t  oid_len_len;
-  u16_t oid_value_len;
-  u8_t  value_len_len;
-  u16_t value_value_len;
-};
-
-/** Agent community string */
-extern const char *snmp_community;
-/** Agent community string for write access */
-extern const char *snmp_community_write;
-/** handle for sending traps */
-extern void *snmp_traps_handle;
-
-void snmp_receive(void *handle, struct pbuf *p, const ip_addr_t *source_ip, u16_t port);
-err_t snmp_sendto(void *handle, struct pbuf *p, const ip_addr_t *dst, u16_t port);
-u8_t snmp_get_local_ip_for_dst(void *handle, const ip_addr_t *dst, ip_addr_t *result);
-err_t snmp_varbind_length(struct snmp_varbind *varbind, struct snmp_varbind_len *len);
-err_t snmp_append_outbound_varbind(struct snmp_pbuf_stream *pbuf_stream, struct snmp_varbind *varbind);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* LWIP_SNMP */
-
-#endif /* LWIP_HDR_APPS_SNMP_MSG_H */
+/**
+ * @file
+ * SNMP Agent message handling structures (internal API, do not use in client code).
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * Copyright (c) 2016 Elias Oenal.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Christiaan Simons <christiaan.simons@axon.tv>
+ *         Martin Hentschel <info@cl-soft.de>
+ *         Elias Oenal <lwip@eliasoenal.com>
+ */
+
+#ifndef LWIP_HDR_APPS_SNMP_MSG_H
+#define LWIP_HDR_APPS_SNMP_MSG_H
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP
+
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "snmp_pbuf_stream.h"
+#include "lwip/ip_addr.h"
+#include "lwip/err.h"
+
+#if LWIP_SNMP_V3
+#include "snmpv3_priv.h"
+#endif
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* The listen port of the SNMP agent. Clients have to make their requests to
+   this port. Most standard clients won't work if you change this! */
+#ifndef SNMP_IN_PORT
+#define SNMP_IN_PORT 161
+#endif
+/* The remote port the SNMP agent sends traps to. Most standard trap sinks won't
+   work if you change this! */
+#ifndef SNMP_TRAP_PORT
+#define SNMP_TRAP_PORT 162
+#endif
+
+/* version defines used in PDU */
+#define SNMP_VERSION_1  0
+#define SNMP_VERSION_2c 1
+#define SNMP_VERSION_3  3
+
+struct snmp_varbind_enumerator
+{
+  struct snmp_pbuf_stream pbuf_stream;
+  u16_t varbind_count;
+};
+
+typedef enum {
+  SNMP_VB_ENUMERATOR_ERR_OK            = 0,
+  SNMP_VB_ENUMERATOR_ERR_EOVB          = 1,
+  SNMP_VB_ENUMERATOR_ERR_ASN1ERROR     = 2,
+  SNMP_VB_ENUMERATOR_ERR_INVALIDLENGTH = 3
+} snmp_vb_enumerator_err_t;
+
+void snmp_vb_enumerator_init(struct snmp_varbind_enumerator* enumerator, struct pbuf* p, u16_t offset, u16_t length);
+snmp_vb_enumerator_err_t snmp_vb_enumerator_get_next(struct snmp_varbind_enumerator* enumerator, struct snmp_varbind* varbind);
+
+struct snmp_request
+{
+  /* Communication handle */
+  void *handle;
+  /* source IP address */
+  const ip_addr_t *source_ip;
+  /* source UDP port */
+  u16_t source_port;
+  /* incoming snmp version */
+  u8_t version;
+  /* community name (zero terminated) */
+  u8_t community[SNMP_MAX_COMMUNITY_STR_LEN + 1];
+  /* community string length (exclusive zero term) */
+  u16_t community_strlen;
+  /* request type */
+  u8_t request_type;
+  /* request ID */
+  s32_t request_id;
+  /* error status */
+  s32_t error_status;
+  /* error index */
+  s32_t error_index;
+  /* non-repeaters (getBulkRequest (SNMPv2c)) */
+  s32_t non_repeaters;
+  /* max-repetitions (getBulkRequest (SNMPv2c)) */
+  s32_t max_repetitions;
+  
+#if LWIP_SNMP_V3
+  s32_t msg_id;
+  s32_t msg_max_size;
+  u8_t  msg_flags;
+  s32_t msg_security_model;
+  u8_t  msg_authoritative_engine_id[SNMP_V3_MAX_ENGINE_ID_LENGTH];
+  u8_t  msg_authoritative_engine_id_len;
+  s32_t msg_authoritative_engine_boots;
+  s32_t msg_authoritative_engine_time;
+  u8_t  msg_user_name[SNMP_V3_MAX_USER_LENGTH];
+  u8_t  msg_user_name_len;
+  u8_t  msg_authentication_parameters[SNMP_V3_MAX_AUTH_PARAM_LENGTH];
+  u8_t  msg_privacy_parameters[SNMP_V3_MAX_PRIV_PARAM_LENGTH];
+  u8_t  context_engine_id[SNMP_V3_MAX_ENGINE_ID_LENGTH];
+  u8_t  context_engine_id_len;
+  u8_t  context_name[SNMP_V3_MAX_ENGINE_ID_LENGTH];
+  u8_t  context_name_len;
+#endif
+
+  struct pbuf *inbound_pbuf;
+  struct snmp_varbind_enumerator inbound_varbind_enumerator;
+  u16_t inbound_varbind_offset;
+  u16_t inbound_varbind_len;
+  u16_t inbound_padding_len;
+
+  struct pbuf *outbound_pbuf;
+  struct snmp_pbuf_stream outbound_pbuf_stream;
+  u16_t outbound_pdu_offset;
+  u16_t outbound_error_status_offset;
+  u16_t outbound_error_index_offset;
+  u16_t outbound_varbind_offset;
+#if LWIP_SNMP_V3
+  u16_t outbound_msg_global_data_offset;
+  u16_t outbound_msg_global_data_end;
+  u16_t outbound_msg_security_parameters_str_offset;
+  u16_t outbound_msg_security_parameters_seq_offset;
+  u16_t outbound_msg_security_parameters_end;
+  u16_t outbound_msg_authentication_parameters_offset;
+  u16_t outbound_scoped_pdu_seq_offset;
+  u16_t outbound_scoped_pdu_string_offset;
+#endif
+
+  u8_t value_buffer[SNMP_MAX_VALUE_SIZE];
+};
+
+/** A helper struct keeping length information about varbinds */
+struct snmp_varbind_len
+{
+  u8_t  vb_len_len;
+  u16_t vb_value_len;
+  u8_t  oid_len_len;
+  u16_t oid_value_len;
+  u8_t  value_len_len;
+  u16_t value_value_len;
+};
+
+/** Agent community string */
+extern const char *snmp_community;
+/** Agent community string for write access */
+extern const char *snmp_community_write;
+/** handle for sending traps */
+extern void* snmp_traps_handle;
+
+void snmp_receive(void *handle, struct pbuf *p, const ip_addr_t *source_ip, u16_t port);
+err_t snmp_sendto(void *handle, struct pbuf *p, const ip_addr_t *dst, u16_t port);
+u8_t snmp_get_local_ip_for_dst(void* handle, const ip_addr_t *dst, ip_addr_t *result);
+err_t snmp_varbind_length(struct snmp_varbind *varbind, struct snmp_varbind_len *len);
+err_t snmp_append_outbound_varbind(struct snmp_pbuf_stream *pbuf_stream, struct snmp_varbind* varbind);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_SNMP */
+
+#endif /* LWIP_HDR_APPS_SNMP_MSG_H */

+ 121 - 123
libs/thirdparty/lwip_2.1.2/src/apps/snmp/snmp_netconn.c → libs/thirdparty/LwIP/src/apps/snmp/snmp_netconn.c

@@ -1,123 +1,121 @@
-/**
- * @file
- * SNMP netconn frontend.
- */
-
-/*
- * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * Author: Dirk Ziegelmeier <dziegel@gmx.de>
- */
-
-#include "lwip/apps/snmp_opts.h"
-
-#if LWIP_SNMP && SNMP_USE_NETCONN
-
-#include <string.h>
-#include "lwip/api.h"
-#include "lwip/ip.h"
-#include "lwip/udp.h"
-#include "snmp_msg.h"
-#include "lwip/sys.h"
-#include "lwip/prot/iana.h"
-
-/** SNMP netconn API worker thread */
-static void
-snmp_netconn_thread(void *arg)
-{
-  struct netconn *conn;
-  struct netbuf *buf;
-  err_t err;
-  LWIP_UNUSED_ARG(arg);
-
-  /* Bind to SNMP port with default IP address */
-#if LWIP_IPV6
-  conn = netconn_new(NETCONN_UDP_IPV6);
-  netconn_bind(conn, IP6_ADDR_ANY, LWIP_IANA_PORT_SNMP);
-#else /* LWIP_IPV6 */
-  conn = netconn_new(NETCONN_UDP);
-  netconn_bind(conn, IP4_ADDR_ANY, LWIP_IANA_PORT_SNMP);
-#endif /* LWIP_IPV6 */
-  LWIP_ERROR("snmp_netconn: invalid conn", (conn != NULL), return;);
-
-  snmp_traps_handle = conn;
-
-  do {
-    err = netconn_recv(conn, &buf);
-
-    if (err == ERR_OK) {
-      snmp_receive(conn, buf->p, &buf->addr, buf->port);
-    }
-
-    if (buf != NULL) {
-      netbuf_delete(buf);
-    }
-  } while (1);
-}
-
-err_t
-snmp_sendto(void *handle, struct pbuf *p, const ip_addr_t *dst, u16_t port)
-{
-  err_t result;
-  struct netbuf buf;
-
-  memset(&buf, 0, sizeof(buf));
-  buf.p = p;
-  result = netconn_sendto((struct netconn *)handle, &buf, dst, port);
-
-  return result;
-}
-
-u8_t
-snmp_get_local_ip_for_dst(void *handle, const ip_addr_t *dst, ip_addr_t *result)
-{
-  struct netconn *conn = (struct netconn *)handle;
-  struct netif *dst_if;
-  const ip_addr_t *dst_ip;
-
-  LWIP_UNUSED_ARG(conn); /* unused in case of IPV4 only configuration */
-
-  ip_route_get_local_ip(&conn->pcb.udp->local_ip, dst, dst_if, dst_ip);
-
-  if ((dst_if != NULL) && (dst_ip != NULL)) {
-    ip_addr_copy(*result, *dst_ip);
-    return 1;
-  } else {
-    return 0;
-  }
-}
-
-/**
- * Starts SNMP Agent.
- */
-void
-snmp_init(void)
-{
-  LWIP_ASSERT_CORE_LOCKED();
-  sys_thread_new("snmp_netconn", snmp_netconn_thread, NULL, SNMP_STACK_SIZE, SNMP_THREAD_PRIO);
-}
-
-#endif /* LWIP_SNMP && SNMP_USE_NETCONN */
+/**
+ * @file
+ * SNMP netconn frontend.
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Dirk Ziegelmeier <dziegel@gmx.de>
+ */
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP && SNMP_USE_NETCONN
+
+#include <string.h>
+#include "lwip/api.h"
+#include "lwip/ip.h"
+#include "lwip/udp.h"
+#include "snmp_msg.h"
+#include "lwip/sys.h"
+
+/** SNMP netconn API worker thread */
+static void
+snmp_netconn_thread(void *arg)
+{
+  struct netconn *conn;
+  struct netbuf *buf;
+  err_t err;
+  LWIP_UNUSED_ARG(arg);
+  
+  /* Bind to SNMP port with default IP address */
+#if LWIP_IPV6
+  conn = netconn_new(NETCONN_UDP_IPV6);
+  netconn_bind(conn, IP6_ADDR_ANY, SNMP_IN_PORT);
+#else /* LWIP_IPV6 */
+  conn = netconn_new(NETCONN_UDP);
+  netconn_bind(conn, IP4_ADDR_ANY, SNMP_IN_PORT);
+#endif /* LWIP_IPV6 */
+  LWIP_ERROR("snmp_netconn: invalid conn", (conn != NULL), return;);
+  
+  snmp_traps_handle = conn;
+
+  do {
+    err = netconn_recv(conn, &buf);
+
+    if (err == ERR_OK) {
+      snmp_receive(conn, buf->p, &buf->addr, buf->port);
+    }
+
+    if (buf != NULL) {
+      netbuf_delete(buf);
+    }
+  } while(1);
+}
+
+err_t 
+snmp_sendto(void *handle, struct pbuf *p, const ip_addr_t *dst, u16_t port)
+{
+  err_t result;
+  struct netbuf buf;
+  
+  memset(&buf, 0, sizeof(buf));
+  buf.p = p;
+  result = netconn_sendto((struct netconn*)handle, &buf, dst, port);
+  
+  return result;
+}
+
+u8_t
+snmp_get_local_ip_for_dst(void* handle, const ip_addr_t *dst, ip_addr_t *result)
+{
+  struct netconn* conn = (struct netconn*)handle;
+  struct netif *dst_if;
+  const ip_addr_t* dst_ip;
+
+  LWIP_UNUSED_ARG(conn); /* unused in case of IPV4 only configuration */
+
+  ip_route_get_local_ip(&conn->pcb.udp->local_ip, dst, dst_if, dst_ip);
+
+  if ((dst_if != NULL) && (dst_ip != NULL)) {
+    ip_addr_copy(*result, *dst_ip);
+    return 1;
+  } else {
+    return 0;
+  }
+}
+
+/**
+ * Starts SNMP Agent.
+ */
+void
+snmp_init(void)
+{
+  sys_thread_new("snmp_netconn", snmp_netconn_thread, NULL, SNMP_STACK_SIZE, SNMP_THREAD_PRIO);
+}
+
+#endif /* LWIP_SNMP && SNMP_USE_NETCONN */

+ 156 - 156
libs/thirdparty/lwip_2.1.2/src/apps/snmp/snmp_pbuf_stream.c → libs/thirdparty/LwIP/src/apps/snmp/snmp_pbuf_stream.c

@@ -1,156 +1,156 @@
-/**
- * @file
- * SNMP pbuf stream wrapper implementation (internal API, do not use in client code).
- */
-
-/*
- * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- *
- * Author: Martin Hentschel <info@cl-soft.de>
- *
- */
-
-#include "lwip/apps/snmp_opts.h"
-
-#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
-
-#include "snmp_pbuf_stream.h"
-#include "lwip/def.h"
-#include <string.h>
-
-err_t
-snmp_pbuf_stream_init(struct snmp_pbuf_stream *pbuf_stream, struct pbuf *p, u16_t offset, u16_t length)
-{
-  pbuf_stream->offset = offset;
-  pbuf_stream->length = length;
-  pbuf_stream->pbuf   = p;
-
-  return ERR_OK;
-}
-
-err_t
-snmp_pbuf_stream_read(struct snmp_pbuf_stream *pbuf_stream, u8_t *data)
-{
-  if (pbuf_stream->length == 0) {
-    return ERR_BUF;
-  }
-
-  if (pbuf_copy_partial(pbuf_stream->pbuf, data, 1, pbuf_stream->offset) == 0) {
-    return ERR_BUF;
-  }
-
-  pbuf_stream->offset++;
-  pbuf_stream->length--;
-
-  return ERR_OK;
-}
-
-err_t
-snmp_pbuf_stream_write(struct snmp_pbuf_stream *pbuf_stream, u8_t data)
-{
-  return snmp_pbuf_stream_writebuf(pbuf_stream, &data, 1);
-}
-
-err_t
-snmp_pbuf_stream_writebuf(struct snmp_pbuf_stream *pbuf_stream, const void *buf, u16_t buf_len)
-{
-  if (pbuf_stream->length < buf_len) {
-    return ERR_BUF;
-  }
-
-  if (pbuf_take_at(pbuf_stream->pbuf, buf, buf_len, pbuf_stream->offset) != ERR_OK) {
-    return ERR_BUF;
-  }
-
-  pbuf_stream->offset += buf_len;
-  pbuf_stream->length -= buf_len;
-
-  return ERR_OK;
-}
-
-err_t
-snmp_pbuf_stream_writeto(struct snmp_pbuf_stream *pbuf_stream, struct snmp_pbuf_stream *target_pbuf_stream, u16_t len)
-{
-
-  if ((pbuf_stream == NULL) || (target_pbuf_stream == NULL)) {
-    return ERR_ARG;
-  }
-  if ((len > pbuf_stream->length) || (len > target_pbuf_stream->length)) {
-    return ERR_ARG;
-  }
-
-  if (len == 0) {
-    len = LWIP_MIN(pbuf_stream->length, target_pbuf_stream->length);
-  }
-
-  while (len > 0) {
-    u16_t chunk_len;
-    err_t err;
-    u16_t target_offset;
-    struct pbuf *pbuf = pbuf_skip(pbuf_stream->pbuf, pbuf_stream->offset, &target_offset);
-
-    if ((pbuf == NULL) || (pbuf->len == 0)) {
-      return ERR_BUF;
-    }
-
-    chunk_len = LWIP_MIN(len, pbuf->len);
-    err = snmp_pbuf_stream_writebuf(target_pbuf_stream, &((u8_t *)pbuf->payload)[target_offset], chunk_len);
-    if (err != ERR_OK) {
-      return err;
-    }
-
-    pbuf_stream->offset   += chunk_len;
-    pbuf_stream->length   -= chunk_len;
-    len -= chunk_len;
-  }
-
-  return ERR_OK;
-}
-
-err_t
-snmp_pbuf_stream_seek(struct snmp_pbuf_stream *pbuf_stream, s32_t offset)
-{
-  if ((offset < 0) || (offset > pbuf_stream->length)) {
-    /* we cannot seek backwards or forward behind stream end */
-    return ERR_ARG;
-  }
-
-  pbuf_stream->offset += (u16_t)offset;
-  pbuf_stream->length -= (u16_t)offset;
-
-  return ERR_OK;
-}
-
-err_t
-snmp_pbuf_stream_seek_abs(struct snmp_pbuf_stream *pbuf_stream, u32_t offset)
-{
-  s32_t rel_offset = offset - pbuf_stream->offset;
-  return snmp_pbuf_stream_seek(pbuf_stream, rel_offset);
-}
-
-#endif /* LWIP_SNMP */
+/**
+ * @file
+ * SNMP pbuf stream wrapper implementation (internal API, do not use in client code).
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Martin Hentschel <info@cl-soft.de>
+ *
+ */
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "snmp_pbuf_stream.h"
+#include "lwip/def.h"
+#include <string.h>
+
+err_t
+snmp_pbuf_stream_init(struct snmp_pbuf_stream* pbuf_stream, struct pbuf* p, u16_t offset, u16_t length)
+{
+  pbuf_stream->offset = offset;
+  pbuf_stream->length = length;
+  pbuf_stream->pbuf   = p;
+
+  return ERR_OK;
+}
+
+err_t
+snmp_pbuf_stream_read(struct snmp_pbuf_stream* pbuf_stream, u8_t* data)
+{
+  if (pbuf_stream->length == 0) {
+    return ERR_BUF;
+  }
+
+  if (pbuf_copy_partial(pbuf_stream->pbuf, data, 1, pbuf_stream->offset) == 0) {
+    return ERR_BUF;
+  }
+
+  pbuf_stream->offset++;
+  pbuf_stream->length--;
+
+  return ERR_OK;
+}
+
+err_t
+snmp_pbuf_stream_write(struct snmp_pbuf_stream* pbuf_stream, u8_t data)
+{
+  return snmp_pbuf_stream_writebuf(pbuf_stream, &data, 1);
+}
+
+err_t
+snmp_pbuf_stream_writebuf(struct snmp_pbuf_stream* pbuf_stream, const void* buf, u16_t buf_len)
+{
+  if (pbuf_stream->length < buf_len) {
+    return ERR_BUF;
+  }
+
+  if (pbuf_take_at(pbuf_stream->pbuf, buf, buf_len, pbuf_stream->offset) != ERR_OK) {
+    return ERR_BUF;
+  }
+
+  pbuf_stream->offset += buf_len;
+  pbuf_stream->length -= buf_len;
+
+  return ERR_OK;
+}
+
+err_t
+snmp_pbuf_stream_writeto(struct snmp_pbuf_stream* pbuf_stream, struct snmp_pbuf_stream* target_pbuf_stream, u16_t len)
+{
+
+  if ((pbuf_stream == NULL) || (target_pbuf_stream == NULL)) {
+    return ERR_ARG;
+  }
+  if ((len > pbuf_stream->length) || (len > target_pbuf_stream->length)) {
+    return ERR_ARG;
+  }
+
+  if (len == 0) {
+    len = LWIP_MIN(pbuf_stream->length, target_pbuf_stream->length);
+  }
+
+  while (len > 0) {
+    u16_t chunk_len;
+    err_t err;
+    u16_t target_offset;
+    struct pbuf* pbuf = pbuf_skip(pbuf_stream->pbuf, pbuf_stream->offset, &target_offset);
+
+    if ((pbuf == NULL) || (pbuf->len == 0)) {
+      return ERR_BUF;
+    }
+
+    chunk_len = LWIP_MIN(len, pbuf->len);
+    err = snmp_pbuf_stream_writebuf(target_pbuf_stream, &((u8_t*)pbuf->payload)[target_offset], chunk_len);
+    if (err != ERR_OK) {
+      return err;
+    }
+
+    pbuf_stream->offset   += chunk_len;
+    pbuf_stream->length   -= chunk_len;
+    len -= chunk_len;
+  }
+
+  return ERR_OK;
+}
+
+err_t
+snmp_pbuf_stream_seek(struct snmp_pbuf_stream* pbuf_stream, s32_t offset)
+{
+  if ((offset < 0) || (offset > pbuf_stream->length)) {
+    /* we cannot seek backwards or forward behind stream end */
+    return ERR_ARG;
+  }
+
+  pbuf_stream->offset += (u16_t)offset;
+  pbuf_stream->length -= (u16_t)offset;
+
+  return ERR_OK;
+}
+
+err_t
+snmp_pbuf_stream_seek_abs(struct snmp_pbuf_stream* pbuf_stream, u32_t offset)
+{
+  s32_t rel_offset = offset - pbuf_stream->offset;
+  return snmp_pbuf_stream_seek(pbuf_stream, rel_offset);
+}
+
+#endif /* LWIP_SNMP */

+ 73 - 72
libs/thirdparty/lwip_2.1.2/src/apps/snmp/snmp_pbuf_stream.h → libs/thirdparty/LwIP/src/apps/snmp/snmp_pbuf_stream.h

@@ -1,72 +1,73 @@
-/**
- * @file
- * SNMP pbuf stream wrapper (internal API, do not use in client code).
- */
-
-/*
- * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- *
- * Author: Martin Hentschel <info@cl-soft.de>
- *
- */
-
-#ifndef LWIP_HDR_APPS_SNMP_PBUF_STREAM_H
-#define LWIP_HDR_APPS_SNMP_PBUF_STREAM_H
-
-#include "lwip/apps/snmp_opts.h"
-
-#if LWIP_SNMP
-
-#include "lwip/err.h"
-#include "lwip/pbuf.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-struct snmp_pbuf_stream {
-  struct pbuf *pbuf;
-  u16_t offset;
-  u16_t length;
-};
-
-err_t snmp_pbuf_stream_init(struct snmp_pbuf_stream *pbuf_stream, struct pbuf *p, u16_t offset, u16_t length);
-err_t snmp_pbuf_stream_read(struct snmp_pbuf_stream *pbuf_stream, u8_t *data);
-err_t snmp_pbuf_stream_write(struct snmp_pbuf_stream *pbuf_stream, u8_t data);
-err_t snmp_pbuf_stream_writebuf(struct snmp_pbuf_stream *pbuf_stream, const void *buf, u16_t buf_len);
-err_t snmp_pbuf_stream_writeto(struct snmp_pbuf_stream *pbuf_stream, struct snmp_pbuf_stream *target_pbuf_stream, u16_t len);
-err_t snmp_pbuf_stream_seek(struct snmp_pbuf_stream *pbuf_stream, s32_t offset);
-err_t snmp_pbuf_stream_seek_abs(struct snmp_pbuf_stream *pbuf_stream, u32_t offset);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* LWIP_SNMP */
-
-#endif /* LWIP_HDR_APPS_SNMP_PBUF_STREAM_H */
+/**
+ * @file
+ * SNMP pbuf stream wrapper (internal API, do not use in client code).
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Martin Hentschel <info@cl-soft.de>
+ *
+ */
+
+#ifndef LWIP_HDR_APPS_SNMP_PBUF_STREAM_H
+#define LWIP_HDR_APPS_SNMP_PBUF_STREAM_H
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP
+
+#include "lwip/err.h"
+#include "lwip/pbuf.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct snmp_pbuf_stream
+{
+  struct pbuf* pbuf;
+  u16_t offset;
+  u16_t length;
+};
+
+err_t snmp_pbuf_stream_init(struct snmp_pbuf_stream* pbuf_stream, struct pbuf* p, u16_t offset, u16_t length);
+err_t snmp_pbuf_stream_read(struct snmp_pbuf_stream* pbuf_stream, u8_t* data);
+err_t snmp_pbuf_stream_write(struct snmp_pbuf_stream* pbuf_stream, u8_t data);
+err_t snmp_pbuf_stream_writebuf(struct snmp_pbuf_stream* pbuf_stream, const void* buf, u16_t buf_len);
+err_t snmp_pbuf_stream_writeto(struct snmp_pbuf_stream* pbuf_stream, struct snmp_pbuf_stream* target_pbuf_stream, u16_t len);
+err_t snmp_pbuf_stream_seek(struct snmp_pbuf_stream* pbuf_stream, s32_t offset);
+err_t snmp_pbuf_stream_seek_abs(struct snmp_pbuf_stream* pbuf_stream, u32_t offset);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_SNMP */
+
+#endif /* LWIP_HDR_APPS_SNMP_PBUF_STREAM_H */

+ 100 - 115
libs/thirdparty/lwip_2.1.2/src/apps/snmp/snmp_raw.c → libs/thirdparty/LwIP/src/apps/snmp/snmp_raw.c

@@ -1,115 +1,100 @@
-/**
- * @file
- * SNMP RAW API frontend.
- */
-
-/*
- * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * Author: Dirk Ziegelmeier <dziegel@gmx.de>
- */
-
-#include "lwip/apps/snmp_opts.h"
-#include "lwip/ip_addr.h"
-
-#if LWIP_SNMP && SNMP_USE_RAW
-
-#include "lwip/udp.h"
-#include "lwip/ip.h"
-#include "lwip/prot/iana.h"
-#include "snmp_msg.h"
-
-/* lwIP UDP receive callback function */
-static void
-snmp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
-{
-  LWIP_UNUSED_ARG(arg);
-  printf("ip: 0x%08x, port:%u\n", addr->addr, port);
-  snmp_receive(pcb, p, addr, port);
-
-  pbuf_free(p);
-}
-
-err_t
-snmp_sendto(void *handle, struct pbuf *p, const ip_addr_t *dst, u16_t port)
-{
-  return udp_sendto((struct udp_pcb *)handle, p, dst, port);
-}
-
-u8_t
-snmp_get_local_ip_for_dst(void *handle, const ip_addr_t *dst, ip_addr_t *result)
-{
-  struct udp_pcb *udp_pcb = (struct udp_pcb *)handle;
-  struct netif *dst_if;
-  const ip_addr_t *dst_ip;
-
-  LWIP_UNUSED_ARG(udp_pcb); /* unused in case of IPV4 only configuration */
-
-  ip_route_get_local_ip(&udp_pcb->local_ip, dst, dst_if, dst_ip);
-
-  if ((dst_if != NULL) && (dst_ip != NULL)) {
-    ip_addr_copy(*result, *dst_ip);
-    return 1;
-  } else {
-    return 0;
-  }
-}
-
-/**
- * @ingroup snmp_core
- * Starts SNMP Agent.
- * Allocates UDP pcb and binds it to IP_ANY_TYPE port 161.
- */
-void
-snmp_init(void)
-{
-  err_t err;
-
-  struct udp_pcb *snmp_pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
-  LWIP_ERROR("snmp_raw: no PCB", (snmp_pcb != NULL), return;);
-
-  LWIP_ASSERT_CORE_LOCKED();
-
-  snmp_traps_handle = snmp_pcb;
-
-  err = udp_bind(snmp_pcb, IP_ANY_TYPE, LWIP_IANA_PORT_SNMP);
-
-  if(err == ERR_OK)
-  {
-
-    /* Set a receive callback for the snmp_pcb */
-    udp_recv(snmp_pcb, snmp_recv, NULL);
-  }
-  else
-  {
-    udp_remove(snmp_pcb);
-    printf("can not bind pcb");
-  }
-
-  LWIP_ERROR("snmp_raw: Unable to bind PCB", (err == ERR_OK), return;);
-}
-
-#endif /* LWIP_SNMP && SNMP_USE_RAW */
+/**
+ * @file
+ * SNMP RAW API frontend.
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Dirk Ziegelmeier <dziegel@gmx.de>
+ */
+
+#include "lwip/apps/snmp_opts.h"
+#include "lwip/ip_addr.h"
+
+#if LWIP_SNMP && SNMP_USE_RAW
+
+#include "lwip/udp.h"
+#include "lwip/ip.h"
+#include "snmp_msg.h"
+
+/* lwIP UDP receive callback function */
+static void
+snmp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
+{
+  LWIP_UNUSED_ARG(arg);
+
+  snmp_receive(pcb, p, addr, port);
+
+  pbuf_free(p);
+}
+
+err_t 
+snmp_sendto(void *handle, struct pbuf *p, const ip_addr_t *dst, u16_t port)
+{
+  return udp_sendto((struct udp_pcb*)handle, p, dst, port);
+}
+
+u8_t
+snmp_get_local_ip_for_dst(void* handle, const ip_addr_t *dst, ip_addr_t *result)
+{
+  struct udp_pcb* udp_pcb = (struct udp_pcb*)handle;
+  struct netif *dst_if;
+  const ip_addr_t* dst_ip;
+
+  LWIP_UNUSED_ARG(udp_pcb); /* unused in case of IPV4 only configuration */
+
+  ip_route_get_local_ip(&udp_pcb->local_ip, dst, dst_if, dst_ip);
+
+  if ((dst_if != NULL) && (dst_ip != NULL)) {
+    ip_addr_copy(*result, *dst_ip);
+    return 1;
+  } else {
+    return 0;
+  }
+}
+
+/**
+ * @ingroup snmp_core
+ * Starts SNMP Agent.
+ * Allocates UDP pcb and binds it to IP_ANY_TYPE port 161.
+ */
+void
+snmp_init(void)
+{
+  err_t err;
+  
+  struct udp_pcb *snmp_pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
+  LWIP_ERROR("snmp_raw: no PCB", (snmp_pcb != NULL), return;);
+
+  snmp_traps_handle = snmp_pcb;
+
+  udp_recv(snmp_pcb, snmp_recv, (void *)SNMP_IN_PORT);
+  err = udp_bind(snmp_pcb, IP_ANY_TYPE, SNMP_IN_PORT);
+  LWIP_ERROR("snmp_raw: Unable to bind PCB", (err == ERR_OK), return;);
+}
+
+#endif /* LWIP_SNMP && SNMP_USE_RAW */

+ 220 - 232
libs/thirdparty/lwip_2.1.2/src/apps/snmp/snmp_scalar.c → libs/thirdparty/LwIP/src/apps/snmp/snmp_scalar.c

@@ -1,232 +1,220 @@
-/**
- * @file
- * SNMP scalar node support implementation.
- */
-
-/*
- * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- *
- * Author: Martin Hentschel <info@cl-soft.de>
- *
- */
-
-#include "lwip/apps/snmp_opts.h"
-
-#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
-
-#include "lwip/apps/snmp_scalar.h"
-#include "lwip/apps/snmp_core.h"
-
-static s16_t snmp_scalar_array_get_value(struct snmp_node_instance *instance, void *value);
-static snmp_err_t  snmp_scalar_array_set_test(struct snmp_node_instance *instance, u16_t value_len, void *value);
-static snmp_err_t  snmp_scalar_array_set_value(struct snmp_node_instance *instance, u16_t value_len, void *value);
-
-snmp_err_t
-snmp_scalar_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance *instance)
-{
-  const struct snmp_scalar_node *scalar_node = (const struct snmp_scalar_node *)(const void *)instance->node;
-
-  LWIP_UNUSED_ARG(root_oid);
-  LWIP_UNUSED_ARG(root_oid_len);
-
-  /* scalar only has one dedicated instance: .0 */
-  if ((instance->instance_oid.len != 1) || (instance->instance_oid.id[0] != 0)) {
-    return SNMP_ERR_NOSUCHINSTANCE;
-  }
-
-  instance->access    = scalar_node->access;
-  instance->asn1_type = scalar_node->asn1_type;
-  instance->get_value = scalar_node->get_value;
-  instance->set_test  = scalar_node->set_test;
-  instance->set_value = scalar_node->set_value;
-  return SNMP_ERR_NOERROR;
-}
-
-snmp_err_t
-snmp_scalar_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance *instance)
-{
-  /* because our only instance is .0 we can only return a next instance if no instance oid is passed */
-  if (instance->instance_oid.len == 0) {
-    instance->instance_oid.len   = 1;
-    instance->instance_oid.id[0] = 0;
-
-    return snmp_scalar_get_instance(root_oid, root_oid_len, instance);
-  }
-
-  return SNMP_ERR_NOSUCHINSTANCE;
-}
-
-
-snmp_err_t
-snmp_scalar_array_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance *instance)
-{
-  LWIP_UNUSED_ARG(root_oid);
-  LWIP_UNUSED_ARG(root_oid_len);
-
-  if ((instance->instance_oid.len == 2) && (instance->instance_oid.id[1] == 0)) {
-    const struct snmp_scalar_array_node *array_node = (const struct snmp_scalar_array_node *)(const void *)instance->node;
-    const struct snmp_scalar_array_node_def *array_node_def = array_node->array_nodes;
-    u32_t i = 0;
-
-    while (i < array_node->array_node_count) {
-      if (array_node_def->oid == instance->instance_oid.id[0]) {
-        break;
-      }
-
-      array_node_def++;
-      i++;
-    }
-
-    if (i < array_node->array_node_count) {
-      instance->access              = array_node_def->access;
-      instance->asn1_type           = array_node_def->asn1_type;
-      instance->get_value           = snmp_scalar_array_get_value;
-      instance->set_test            = snmp_scalar_array_set_test;
-      instance->set_value           = snmp_scalar_array_set_value;
-      instance->reference.const_ptr = array_node_def;
-
-      return SNMP_ERR_NOERROR;
-    }
-  }
-
-  return SNMP_ERR_NOSUCHINSTANCE;
-}
-
-snmp_err_t
-snmp_scalar_array_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance *instance)
-{
-  const struct snmp_scalar_array_node *array_node = (const struct snmp_scalar_array_node *)(const void *)instance->node;
-  const struct snmp_scalar_array_node_def *array_node_def = array_node->array_nodes;
-  const struct snmp_scalar_array_node_def *result = NULL;
-
-  LWIP_UNUSED_ARG(root_oid);
-  LWIP_UNUSED_ARG(root_oid_len);
-
-  if ((instance->instance_oid.len == 0) && (array_node->array_node_count > 0)) {
-    /* return node with lowest OID */
-    u16_t i = 0;
-
-    result = array_node_def;
-    array_node_def++;
-
-    for (i = 1; i < array_node->array_node_count; i++) {
-      if (array_node_def->oid < result->oid) {
-        result = array_node_def;
-      }
-      array_node_def++;
-    }
-  } else if (instance->instance_oid.len >= 1) {
-    if (instance->instance_oid.len == 1) {
-      /* if we have the requested OID we return its instance, otherwise we search for the next available */
-      u16_t i = 0;
-      while (i < array_node->array_node_count) {
-        if (array_node_def->oid == instance->instance_oid.id[0]) {
-          result = array_node_def;
-          break;
-        }
-
-        array_node_def++;
-        i++;
-      }
-    }
-    if (result == NULL) {
-      u32_t oid_dist = 0xFFFFFFFFUL;
-      u16_t i        = 0;
-      array_node_def = array_node->array_nodes; /* may be already at the end when if case before was executed without result -> reinitialize to start */
-      while (i < array_node->array_node_count) {
-        if ((array_node_def->oid > instance->instance_oid.id[0]) &&
-            ((u32_t)(array_node_def->oid - instance->instance_oid.id[0]) < oid_dist)) {
-          result   = array_node_def;
-          oid_dist = array_node_def->oid - instance->instance_oid.id[0];
-        }
-
-        array_node_def++;
-        i++;
-      }
-    }
-  }
-
-  if (result == NULL) {
-    /* nothing to return */
-    return SNMP_ERR_NOSUCHINSTANCE;
-  }
-
-  instance->instance_oid.len   = 2;
-  instance->instance_oid.id[0] = result->oid;
-  instance->instance_oid.id[1] = 0;
-
-  instance->access              = result->access;
-  instance->asn1_type           = result->asn1_type;
-  instance->get_value           = snmp_scalar_array_get_value;
-  instance->set_test            = snmp_scalar_array_set_test;
-  instance->set_value           = snmp_scalar_array_set_value;
-  instance->reference.const_ptr = result;
-
-  return SNMP_ERR_NOERROR;
-}
-
-static s16_t
-snmp_scalar_array_get_value(struct snmp_node_instance *instance, void *value)
-{
-  s16_t result = -1;
-  const struct snmp_scalar_array_node *array_node = (const struct snmp_scalar_array_node *)(const void *)instance->node;
-  const struct snmp_scalar_array_node_def *array_node_def = (const struct snmp_scalar_array_node_def *)instance->reference.const_ptr;
-
-  if (array_node->get_value != NULL) {
-    result = array_node->get_value(array_node_def, value);
-  }
-  return result;
-}
-
-static snmp_err_t
-snmp_scalar_array_set_test(struct snmp_node_instance *instance, u16_t value_len, void *value)
-{
-  snmp_err_t result = SNMP_ERR_NOTWRITABLE;
-  const struct snmp_scalar_array_node *array_node = (const struct snmp_scalar_array_node *)(const void *)instance->node;
-  const struct snmp_scalar_array_node_def *array_node_def = (const struct snmp_scalar_array_node_def *)instance->reference.const_ptr;
-
-  if (array_node->set_test != NULL) {
-    result = array_node->set_test(array_node_def, value_len, value);
-  }
-  return result;
-}
-
-static snmp_err_t
-snmp_scalar_array_set_value(struct snmp_node_instance *instance, u16_t value_len, void *value)
-{
-  snmp_err_t result = SNMP_ERR_NOTWRITABLE;
-  const struct snmp_scalar_array_node *array_node = (const struct snmp_scalar_array_node *)(const void *)instance->node;
-  const struct snmp_scalar_array_node_def *array_node_def = (const struct snmp_scalar_array_node_def *)instance->reference.const_ptr;
-
-  if (array_node->set_value != NULL) {
-    result = array_node->set_value(array_node_def, value_len, value);
-  }
-  return result;
-}
-
-#endif /* LWIP_SNMP */
+/**
+ * @file
+ * SNMP scalar node support implementation.
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Martin Hentschel <info@cl-soft.de>
+ *
+ */
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/apps/snmp_scalar.h"
+#include "lwip/apps/snmp_core.h"
+
+static s16_t snmp_scalar_array_get_value(struct snmp_node_instance* instance, void* value);
+static snmp_err_t  snmp_scalar_array_set_test(struct snmp_node_instance* instance, u16_t value_len, void* value);
+static snmp_err_t  snmp_scalar_array_set_value(struct snmp_node_instance* instance, u16_t value_len, void* value);
+
+snmp_err_t 
+snmp_scalar_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance)
+{
+  const struct snmp_scalar_node* scalar_node = (const struct snmp_scalar_node*)(const void*)instance->node;
+
+  LWIP_UNUSED_ARG(root_oid);
+  LWIP_UNUSED_ARG(root_oid_len);
+
+  /* scalar only has one dedicated instance: .0 */
+  if ((instance->instance_oid.len != 1) || (instance->instance_oid.id[0] != 0)) {
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  instance->access    = scalar_node->access;
+  instance->asn1_type = scalar_node->asn1_type;
+  instance->get_value = scalar_node->get_value;
+  instance->set_test  = scalar_node->set_test;
+  instance->set_value = scalar_node->set_value;
+  return SNMP_ERR_NOERROR;
+}
+
+snmp_err_t 
+snmp_scalar_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance)
+{
+  /* because our only instance is .0 we can only return a next instance if no instance oid is passed */
+  if (instance->instance_oid.len == 0) {
+    instance->instance_oid.len   = 1;
+    instance->instance_oid.id[0] = 0;
+
+    return snmp_scalar_get_instance(root_oid, root_oid_len, instance);
+  }
+
+  return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+
+snmp_err_t
+snmp_scalar_array_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance)
+{
+  LWIP_UNUSED_ARG(root_oid);
+  LWIP_UNUSED_ARG(root_oid_len);
+
+  if ((instance->instance_oid.len == 2) && (instance->instance_oid.id[1] == 0)) {
+    const struct snmp_scalar_array_node* array_node = (const struct snmp_scalar_array_node*)(const void*)instance->node;
+    const struct snmp_scalar_array_node_def* array_node_def = array_node->array_nodes;
+    u32_t i = 0;
+
+    while (i < array_node->array_node_count) {
+      if (array_node_def->oid == instance->instance_oid.id[0]) {
+        break;
+      }
+
+      array_node_def++;
+      i++;
+    }
+
+    if (i < array_node->array_node_count) {
+      instance->access              = array_node_def->access;
+      instance->asn1_type           = array_node_def->asn1_type;
+      instance->get_value           = snmp_scalar_array_get_value;
+      instance->set_test            = snmp_scalar_array_set_test;
+      instance->set_value           = snmp_scalar_array_set_value;
+      instance->reference.const_ptr = array_node_def;
+
+      return SNMP_ERR_NOERROR;
+    }
+  }
+
+  return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+snmp_err_t
+snmp_scalar_array_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance)
+{
+  const struct snmp_scalar_array_node* array_node = (const struct snmp_scalar_array_node*)(const void*)instance->node;
+  const struct snmp_scalar_array_node_def* array_node_def = array_node->array_nodes;
+  const struct snmp_scalar_array_node_def* result = NULL;
+
+  LWIP_UNUSED_ARG(root_oid);
+  LWIP_UNUSED_ARG(root_oid_len);
+
+  if ((instance->instance_oid.len == 0) && (array_node->array_node_count > 0)) {
+    /* return node with lowest OID */
+    u16_t i = 0;
+    
+    result = array_node_def;
+    array_node_def++;
+
+    for (i = 1; i < array_node->array_node_count; i++) {
+      if (array_node_def->oid < result->oid) {
+        result = array_node_def;
+      }
+      array_node_def++;
+    }
+  } else if (instance->instance_oid.len >= 1) {
+    if (instance->instance_oid.len == 1) {
+      /* if we have the requested OID we return its instance, otherwise we search for the next available */    
+      u16_t i = 0;
+      while (i < array_node->array_node_count) {
+        if (array_node_def->oid == instance->instance_oid.id[0]) {
+          result = array_node_def;
+          break;
+        }
+
+        array_node_def++;
+        i++;
+      }
+    }
+    if (result == NULL) {
+      u32_t oid_dist = 0xFFFFFFFFUL;
+      u16_t i        = 0;
+      array_node_def = array_node->array_nodes; /* may be already at the end when if case before was executed without result -> reinitialize to start */
+      while (i < array_node->array_node_count) {
+        if ((array_node_def->oid > instance->instance_oid.id[0]) &&
+            ((u32_t)(array_node_def->oid - instance->instance_oid.id[0]) < oid_dist)) {
+          result   = array_node_def;
+          oid_dist = array_node_def->oid - instance->instance_oid.id[0];
+        }
+
+        array_node_def++;
+        i++;
+      }
+    }
+  }
+
+  if (result == NULL) {
+    /* nothing to return */
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  instance->instance_oid.len   = 2;
+  instance->instance_oid.id[0] = result->oid;
+  instance->instance_oid.id[1] = 0;
+  
+  instance->access              = result->access;
+  instance->asn1_type           = result->asn1_type;
+  instance->get_value           = snmp_scalar_array_get_value;
+  instance->set_test            = snmp_scalar_array_set_test;
+  instance->set_value           = snmp_scalar_array_set_value;
+  instance->reference.const_ptr = result;
+
+  return SNMP_ERR_NOERROR;
+}
+
+static s16_t
+snmp_scalar_array_get_value(struct snmp_node_instance* instance, void* value)
+{
+  const struct snmp_scalar_array_node* array_node = (const struct snmp_scalar_array_node*)(const void*)instance->node;
+  const struct snmp_scalar_array_node_def* array_node_def = (const struct snmp_scalar_array_node_def*)instance->reference.const_ptr;
+
+  return array_node->get_value(array_node_def, value);
+}
+
+static snmp_err_t
+snmp_scalar_array_set_test(struct snmp_node_instance* instance, u16_t value_len, void* value)
+{
+  const struct snmp_scalar_array_node* array_node = (const struct snmp_scalar_array_node*)(const void*)instance->node;
+  const struct snmp_scalar_array_node_def* array_node_def = (const struct snmp_scalar_array_node_def*)instance->reference.const_ptr;
+
+  return array_node->set_test(array_node_def, value_len, value);
+}
+
+static snmp_err_t
+snmp_scalar_array_set_value(struct snmp_node_instance* instance, u16_t value_len, void* value)
+{
+  const struct snmp_scalar_array_node* array_node = (const struct snmp_scalar_array_node*)(const void*)instance->node;
+  const struct snmp_scalar_array_node_def* array_node_def = (const struct snmp_scalar_array_node_def*)instance->reference.const_ptr;
+
+  return array_node->set_value(array_node_def, value_len, value);
+}
+
+#endif /* LWIP_SNMP */

+ 343 - 342
libs/thirdparty/lwip_2.1.2/src/apps/snmp/snmp_table.c → libs/thirdparty/LwIP/src/apps/snmp/snmp_table.c

@@ -1,342 +1,343 @@
-/**
- * @file
- * SNMP table support implementation.
- */
-
-/*
- * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- *
- * Author: Martin Hentschel <info@cl-soft.de>
- *
- */
-
-#include "lwip/apps/snmp_opts.h"
-
-#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
-
-#include "lwip/apps/snmp_core.h"
-#include "lwip/apps/snmp_table.h"
-#include <string.h>
-
-snmp_err_t snmp_table_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance *instance)
-{
-  snmp_err_t ret = SNMP_ERR_NOSUCHINSTANCE;
-  const struct snmp_table_node *table_node = (const struct snmp_table_node *)(const void *)instance->node;
-
-  LWIP_UNUSED_ARG(root_oid);
-  LWIP_UNUSED_ARG(root_oid_len);
-
-  /* check min. length (fixed row entry definition, column, row instance oid with at least one entry */
-  /* fixed row entry always has oid 1 */
-  if ((instance->instance_oid.len >= 3) && (instance->instance_oid.id[0] == 1)) {
-    /* search column */
-    const struct snmp_table_col_def *col_def = table_node->columns;
-    u16_t i = table_node->column_count;
-    while (i > 0) {
-      if (col_def->index == instance->instance_oid.id[1]) {
-        break;
-      }
-
-      col_def++;
-      i--;
-    }
-
-    if (i > 0) {
-      /* everything may be overwritten by get_cell_instance_method() in order to implement special handling for single columns/cells */
-      instance->asn1_type = col_def->asn1_type;
-      instance->access    = col_def->access;
-      instance->get_value = table_node->get_value;
-      instance->set_test  = table_node->set_test;
-      instance->set_value = table_node->set_value;
-
-      ret = table_node->get_cell_instance(
-              &(instance->instance_oid.id[1]),
-              &(instance->instance_oid.id[2]),
-              instance->instance_oid.len - 2,
-              instance);
-    }
-  }
-
-  return ret;
-}
-
-snmp_err_t snmp_table_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance *instance)
-{
-  const struct snmp_table_node *table_node = (const struct snmp_table_node *)(const void *)instance->node;
-  const struct snmp_table_col_def *col_def;
-  struct snmp_obj_id row_oid;
-  u32_t column = 0;
-  snmp_err_t result;
-
-  LWIP_UNUSED_ARG(root_oid);
-  LWIP_UNUSED_ARG(root_oid_len);
-
-  /* check that first part of id is 0 or 1, referencing fixed row entry */
-  if ((instance->instance_oid.len > 0) && (instance->instance_oid.id[0] > 1)) {
-    return SNMP_ERR_NOSUCHINSTANCE;
-  }
-  if (instance->instance_oid.len > 1) {
-    column = instance->instance_oid.id[1];
-  }
-  if (instance->instance_oid.len > 2) {
-    snmp_oid_assign(&row_oid, &(instance->instance_oid.id[2]), instance->instance_oid.len - 2);
-  } else {
-    row_oid.len = 0;
-  }
-
-  instance->get_value    = table_node->get_value;
-  instance->set_test     = table_node->set_test;
-  instance->set_value    = table_node->set_value;
-
-  /* resolve column and value */
-  do {
-    u16_t i;
-    const struct snmp_table_col_def *next_col_def = NULL;
-    col_def = table_node->columns;
-
-    for (i = 0; i < table_node->column_count; i++) {
-      if (col_def->index == column) {
-        next_col_def = col_def;
-        break;
-      } else if ((col_def->index > column) && ((next_col_def == NULL) || (col_def->index < next_col_def->index))) {
-        next_col_def = col_def;
-      }
-      col_def++;
-    }
-
-    if (next_col_def == NULL) {
-      /* no further column found */
-      return SNMP_ERR_NOSUCHINSTANCE;
-    }
-
-    instance->asn1_type          = next_col_def->asn1_type;
-    instance->access             = next_col_def->access;
-
-    result = table_node->get_next_cell_instance(
-               &next_col_def->index,
-               &row_oid,
-               instance);
-
-    if (result == SNMP_ERR_NOERROR) {
-      col_def = next_col_def;
-      break;
-    }
-
-    row_oid.len = 0; /* reset row_oid because we switch to next column and start with the first entry there */
-    column = next_col_def->index + 1;
-  } while (1);
-
-  /* build resulting oid */
-  instance->instance_oid.len   = 2;
-  instance->instance_oid.id[0] = 1;
-  instance->instance_oid.id[1] = col_def->index;
-  snmp_oid_append(&instance->instance_oid, row_oid.id, row_oid.len);
-
-  return SNMP_ERR_NOERROR;
-}
-
-
-snmp_err_t snmp_table_simple_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance *instance)
-{
-  snmp_err_t ret = SNMP_ERR_NOSUCHINSTANCE;
-  const struct snmp_table_simple_node *table_node = (const struct snmp_table_simple_node *)(const void *)instance->node;
-
-  LWIP_UNUSED_ARG(root_oid);
-  LWIP_UNUSED_ARG(root_oid_len);
-
-  /* check min. length (fixed row entry definition, column, row instance oid with at least one entry */
-  /* fixed row entry always has oid 1 */
-  if ((instance->instance_oid.len >= 3) && (instance->instance_oid.id[0] == 1)) {
-    ret = table_node->get_cell_value(
-            &(instance->instance_oid.id[1]),
-            &(instance->instance_oid.id[2]),
-            instance->instance_oid.len - 2,
-            &instance->reference,
-            &instance->reference_len);
-
-    if (ret == SNMP_ERR_NOERROR) {
-      /* search column */
-      const struct snmp_table_simple_col_def *col_def = table_node->columns;
-      u32_t i = table_node->column_count;
-      while (i > 0) {
-        if (col_def->index == instance->instance_oid.id[1]) {
-          break;
-        }
-
-        col_def++;
-        i--;
-      }
-
-      if (i > 0) {
-        instance->asn1_type = col_def->asn1_type;
-        instance->access    = SNMP_NODE_INSTANCE_READ_ONLY;
-        instance->set_test  = NULL;
-        instance->set_value = NULL;
-
-        switch (col_def->data_type) {
-          case SNMP_VARIANT_VALUE_TYPE_U32:
-            instance->get_value = snmp_table_extract_value_from_u32ref;
-            break;
-          case SNMP_VARIANT_VALUE_TYPE_S32:
-            instance->get_value = snmp_table_extract_value_from_s32ref;
-            break;
-          case SNMP_VARIANT_VALUE_TYPE_PTR: /* fall through */
-          case SNMP_VARIANT_VALUE_TYPE_CONST_PTR:
-            instance->get_value = snmp_table_extract_value_from_refconstptr;
-            break;
-          default:
-            LWIP_DEBUGF(SNMP_DEBUG, ("snmp_table_simple_get_instance(): unknown column data_type: %d\n", col_def->data_type));
-            return SNMP_ERR_GENERROR;
-        }
-
-        ret = SNMP_ERR_NOERROR;
-      } else {
-        ret = SNMP_ERR_NOSUCHINSTANCE;
-      }
-    }
-  }
-
-  return ret;
-}
-
-snmp_err_t snmp_table_simple_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance *instance)
-{
-  const struct snmp_table_simple_node *table_node = (const struct snmp_table_simple_node *)(const void *)instance->node;
-  const struct snmp_table_simple_col_def *col_def;
-  struct snmp_obj_id row_oid;
-  u32_t column = 0;
-  snmp_err_t result;
-
-  LWIP_UNUSED_ARG(root_oid);
-  LWIP_UNUSED_ARG(root_oid_len);
-
-  /* check that first part of id is 0 or 1, referencing fixed row entry */
-  if ((instance->instance_oid.len > 0) && (instance->instance_oid.id[0] > 1)) {
-    return SNMP_ERR_NOSUCHINSTANCE;
-  }
-  if (instance->instance_oid.len > 1) {
-    column = instance->instance_oid.id[1];
-  }
-  if (instance->instance_oid.len > 2) {
-    snmp_oid_assign(&row_oid, &(instance->instance_oid.id[2]), instance->instance_oid.len - 2);
-  } else {
-    row_oid.len = 0;
-  }
-
-  /* resolve column and value */
-  do {
-    u32_t i;
-    const struct snmp_table_simple_col_def *next_col_def = NULL;
-    col_def = table_node->columns;
-
-    for (i = 0; i < table_node->column_count; i++) {
-      if (col_def->index == column) {
-        next_col_def = col_def;
-        break;
-      } else if ((col_def->index > column) && ((next_col_def == NULL) ||
-                 (col_def->index < next_col_def->index))) {
-        next_col_def = col_def;
-      }
-      col_def++;
-    }
-
-    if (next_col_def == NULL) {
-      /* no further column found */
-      return SNMP_ERR_NOSUCHINSTANCE;
-    }
-
-    result = table_node->get_next_cell_instance_and_value(
-               &next_col_def->index,
-               &row_oid,
-               &instance->reference,
-               &instance->reference_len);
-
-    if (result == SNMP_ERR_NOERROR) {
-      col_def = next_col_def;
-      break;
-    }
-
-    row_oid.len = 0; /* reset row_oid because we switch to next column and start with the first entry there */
-    column = next_col_def->index + 1;
-  } while (1);
-
-  instance->asn1_type = col_def->asn1_type;
-  instance->access    = SNMP_NODE_INSTANCE_READ_ONLY;
-  instance->set_test  = NULL;
-  instance->set_value = NULL;
-
-  switch (col_def->data_type) {
-    case SNMP_VARIANT_VALUE_TYPE_U32:
-      instance->get_value = snmp_table_extract_value_from_u32ref;
-      break;
-    case SNMP_VARIANT_VALUE_TYPE_S32:
-      instance->get_value = snmp_table_extract_value_from_s32ref;
-      break;
-    case SNMP_VARIANT_VALUE_TYPE_PTR: /* fall through */
-    case SNMP_VARIANT_VALUE_TYPE_CONST_PTR:
-      instance->get_value = snmp_table_extract_value_from_refconstptr;
-      break;
-    default:
-      LWIP_DEBUGF(SNMP_DEBUG, ("snmp_table_simple_get_instance(): unknown column data_type: %d\n", col_def->data_type));
-      return SNMP_ERR_GENERROR;
-  }
-
-  /* build resulting oid */
-  instance->instance_oid.len   = 2;
-  instance->instance_oid.id[0] = 1;
-  instance->instance_oid.id[1] = col_def->index;
-  snmp_oid_append(&instance->instance_oid, row_oid.id, row_oid.len);
-
-  return SNMP_ERR_NOERROR;
-}
-
-
-s16_t
-snmp_table_extract_value_from_s32ref(struct snmp_node_instance *instance, void *value)
-{
-  s32_t *dst = (s32_t *)value;
-  *dst = instance->reference.s32;
-  return sizeof(*dst);
-}
-
-s16_t
-snmp_table_extract_value_from_u32ref(struct snmp_node_instance *instance, void *value)
-{
-  u32_t *dst = (u32_t *)value;
-  *dst = instance->reference.u32;
-  return sizeof(*dst);
-}
-
-s16_t
-snmp_table_extract_value_from_refconstptr(struct snmp_node_instance *instance, void *value)
-{
-  MEMCPY(value, instance->reference.const_ptr, instance->reference_len);
-  return (u16_t)instance->reference_len;
-}
-
-#endif /* LWIP_SNMP */
+/**
+ * @file
+ * SNMP table support implementation.
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Martin Hentschel <info@cl-soft.de>
+ *
+ */
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/apps/snmp_core.h"
+#include "lwip/apps/snmp_table.h"
+#include <string.h>
+
+snmp_err_t snmp_table_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance)
+{
+  snmp_err_t ret = SNMP_ERR_NOSUCHINSTANCE;
+  const struct snmp_table_node* table_node = (const struct snmp_table_node*)(const void*)instance->node;
+
+  LWIP_UNUSED_ARG(root_oid);
+  LWIP_UNUSED_ARG(root_oid_len);
+
+  /* check min. length (fixed row entry definition, column, row instance oid with at least one entry */
+  /* fixed row entry always has oid 1 */
+  if ((instance->instance_oid.len >= 3) && (instance->instance_oid.id[0] == 1)) {
+    /* search column */
+    const struct snmp_table_col_def* col_def = table_node->columns;
+    u16_t i = table_node->column_count;
+    while (i > 0) {
+      if (col_def->index == instance->instance_oid.id[1]) {
+        break;
+      }
+      
+      col_def++;
+      i--;
+    }
+
+    if (i > 0) {
+      /* everything may be overwritten by get_cell_instance_method() in order to implement special handling for single columns/cells */
+      instance->asn1_type = col_def->asn1_type;
+      instance->access    = col_def->access;
+      instance->get_value = table_node->get_value;
+      instance->set_test  = table_node->set_test;
+      instance->set_value = table_node->set_value;
+
+      ret = table_node->get_cell_instance(
+        &(instance->instance_oid.id[1]),
+        &(instance->instance_oid.id[2]),
+        instance->instance_oid.len-2,
+        instance);
+    }
+  }
+
+  return ret;
+}
+
+snmp_err_t snmp_table_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance)
+{
+  const struct snmp_table_node* table_node = (const struct snmp_table_node*)(const void*)instance->node;
+  const struct snmp_table_col_def* col_def;
+  struct snmp_obj_id row_oid;
+  u32_t column = 0;
+  snmp_err_t result;
+
+  LWIP_UNUSED_ARG(root_oid);
+  LWIP_UNUSED_ARG(root_oid_len);
+
+  /* check that first part of id is 0 or 1, referencing fixed row entry */
+  if ((instance->instance_oid.len > 0) && (instance->instance_oid.id[0] > 1)) {
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+  if (instance->instance_oid.len > 1) {
+    column = instance->instance_oid.id[1];
+  }
+  if (instance->instance_oid.len > 2) {
+    snmp_oid_assign(&row_oid, &(instance->instance_oid.id[2]), instance->instance_oid.len - 2);
+  } else {
+    row_oid.len = 0;
+  }
+
+  instance->get_value    = table_node->get_value;
+  instance->set_test     = table_node->set_test;
+  instance->set_value    = table_node->set_value;
+
+  /* resolve column and value */
+  do {
+    u16_t i;
+    const struct snmp_table_col_def* next_col_def = NULL;
+    col_def = table_node->columns;
+
+    for (i = 0; i < table_node->column_count; i++) {
+      if (col_def->index == column) {
+        next_col_def = col_def;
+        break;
+      } else if ((col_def->index > column) && ((next_col_def == NULL) || (col_def->index < next_col_def->index))) {
+        next_col_def = col_def;
+      }
+      col_def++;
+    }
+
+    if (next_col_def == NULL) {
+      /* no further column found */
+      return SNMP_ERR_NOSUCHINSTANCE;
+    }
+
+    instance->asn1_type          = next_col_def->asn1_type;
+    instance->access             = next_col_def->access;
+
+    result = table_node->get_next_cell_instance(
+      &next_col_def->index,
+      &row_oid,
+      instance);
+
+    if (result == SNMP_ERR_NOERROR) {
+      col_def = next_col_def;
+      break;
+    }
+
+    row_oid.len = 0; /* reset row_oid because we switch to next column and start with the first entry there */
+    column = next_col_def->index + 1;
+  } while (1);
+
+  /* build resulting oid */
+  instance->instance_oid.len   = 2;
+  instance->instance_oid.id[0] = 1;
+  instance->instance_oid.id[1] = col_def->index;
+  snmp_oid_append(&instance->instance_oid, row_oid.id, row_oid.len);
+
+  return SNMP_ERR_NOERROR;
+}
+
+
+snmp_err_t snmp_table_simple_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance)
+{
+  snmp_err_t ret = SNMP_ERR_NOSUCHINSTANCE;
+  const struct snmp_table_simple_node* table_node = (const struct snmp_table_simple_node*)(const void*)instance->node;
+
+  LWIP_UNUSED_ARG(root_oid);
+  LWIP_UNUSED_ARG(root_oid_len);
+
+  /* check min. length (fixed row entry definition, column, row instance oid with at least one entry */
+  /* fixed row entry always has oid 1 */
+  if ((instance->instance_oid.len >= 3) && (instance->instance_oid.id[0] == 1)) {
+    ret = table_node->get_cell_value(
+      &(instance->instance_oid.id[1]),
+      &(instance->instance_oid.id[2]),
+      instance->instance_oid.len-2,
+      &instance->reference,
+      &instance->reference_len);
+
+    if (ret == SNMP_ERR_NOERROR) {
+      /* search column */
+      const struct snmp_table_simple_col_def* col_def = table_node->columns;
+      u32_t i = table_node->column_count;
+      while (i > 0) {
+        if (col_def->index == instance->instance_oid.id[1]) {
+          break;
+        }
+
+        col_def++;
+        i--;
+      }
+
+      if (i > 0) {
+        instance->asn1_type = col_def->asn1_type;
+        instance->access    = SNMP_NODE_INSTANCE_READ_ONLY;
+        instance->set_test  = NULL;
+        instance->set_value = NULL;
+
+        switch (col_def->data_type) {
+          case SNMP_VARIANT_VALUE_TYPE_U32:
+            instance->get_value = snmp_table_extract_value_from_u32ref;
+            break;
+          case SNMP_VARIANT_VALUE_TYPE_S32:
+            instance->get_value = snmp_table_extract_value_from_s32ref;
+            break;
+          case SNMP_VARIANT_VALUE_TYPE_PTR: /* fall through */
+          case SNMP_VARIANT_VALUE_TYPE_CONST_PTR:
+            instance->get_value = snmp_table_extract_value_from_refconstptr;
+            break;
+          default:
+            LWIP_DEBUGF(SNMP_DEBUG, ("snmp_table_simple_get_instance(): unknown column data_type: %d\n", col_def->data_type));
+            return SNMP_ERR_GENERROR;
+        }        
+
+        ret = SNMP_ERR_NOERROR;
+      } else {
+        ret = SNMP_ERR_NOSUCHINSTANCE;
+      }
+    } 
+  }
+
+  return ret;
+}
+
+snmp_err_t snmp_table_simple_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance)
+{
+  const struct snmp_table_simple_node* table_node = (const struct snmp_table_simple_node*)(const void*)instance->node;
+  const struct snmp_table_simple_col_def* col_def;
+  struct snmp_obj_id row_oid;
+  u32_t column = 0;
+  snmp_err_t result;
+
+  LWIP_UNUSED_ARG(root_oid);
+  LWIP_UNUSED_ARG(root_oid_len);
+
+  /* check that first part of id is 0 or 1, referencing fixed row entry */
+  if ((instance->instance_oid.len > 0) && (instance->instance_oid.id[0] > 1)) {
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+  if (instance->instance_oid.len > 1) {
+    column = instance->instance_oid.id[1];
+  }
+  if (instance->instance_oid.len > 2) {
+    snmp_oid_assign(&row_oid, &(instance->instance_oid.id[2]), instance->instance_oid.len - 2);
+  } else {
+    row_oid.len = 0;
+  }
+
+  /* resolve column and value */
+  do {
+    u32_t i;
+    const struct snmp_table_simple_col_def* next_col_def = NULL;
+    col_def = table_node->columns;
+
+    for (i = 0; i < table_node->column_count; i++) {
+      if (col_def->index == column) {
+        next_col_def = col_def;
+        break;
+      } else if ((col_def->index > column) && ((next_col_def == NULL) ||
+                 (col_def->index < next_col_def->index))) {
+        next_col_def = col_def;
+      }
+      col_def++;
+    }
+
+    if (next_col_def == NULL) {
+      /* no further column found */
+      return SNMP_ERR_NOSUCHINSTANCE;
+    }
+
+    result = table_node->get_next_cell_instance_and_value(
+      &next_col_def->index,
+      &row_oid,
+      &instance->reference,
+      &instance->reference_len);
+
+    if (result == SNMP_ERR_NOERROR) {
+      col_def = next_col_def;
+      break;
+    }
+
+    row_oid.len = 0; /* reset row_oid because we switch to next column and start with the first entry there */
+    column = next_col_def->index + 1;
+  }
+  while (1);
+
+  instance->asn1_type = col_def->asn1_type;
+  instance->access    = SNMP_NODE_INSTANCE_READ_ONLY;
+  instance->set_test  = NULL;
+  instance->set_value = NULL;
+
+  switch (col_def->data_type) {
+    case SNMP_VARIANT_VALUE_TYPE_U32:
+      instance->get_value = snmp_table_extract_value_from_u32ref;
+      break;
+    case SNMP_VARIANT_VALUE_TYPE_S32:
+      instance->get_value = snmp_table_extract_value_from_s32ref;
+      break;
+    case SNMP_VARIANT_VALUE_TYPE_PTR: /* fall through */
+    case SNMP_VARIANT_VALUE_TYPE_CONST_PTR:
+      instance->get_value = snmp_table_extract_value_from_refconstptr;
+      break;
+    default:
+      LWIP_DEBUGF(SNMP_DEBUG, ("snmp_table_simple_get_instance(): unknown column data_type: %d\n", col_def->data_type));
+      return SNMP_ERR_GENERROR;
+  }
+
+  /* build resulting oid */
+  instance->instance_oid.len   = 2;
+  instance->instance_oid.id[0] = 1;
+  instance->instance_oid.id[1] = col_def->index;
+  snmp_oid_append(&instance->instance_oid, row_oid.id, row_oid.len);
+
+  return SNMP_ERR_NOERROR;
+}
+
+
+s16_t
+snmp_table_extract_value_from_s32ref(struct snmp_node_instance* instance, void* value)
+{
+  s32_t *dst = (s32_t*)value;
+  *dst = instance->reference.s32;
+  return sizeof(*dst);
+}
+
+s16_t
+snmp_table_extract_value_from_u32ref(struct snmp_node_instance* instance, void* value)
+{
+  u32_t *dst = (u32_t*)value;
+  *dst = instance->reference.u32;
+  return sizeof(*dst);
+}
+
+s16_t
+snmp_table_extract_value_from_refconstptr(struct snmp_node_instance* instance, void* value)
+{
+  MEMCPY(value, instance->reference.const_ptr, instance->reference_len);
+  return (u16_t)instance->reference_len;
+}
+
+#endif /* LWIP_SNMP */

+ 224 - 231
libs/thirdparty/lwip_2.1.2/src/apps/snmp/snmp_threadsync.c → libs/thirdparty/LwIP/src/apps/snmp/snmp_threadsync.c

@@ -1,231 +1,224 @@
-/**
- * @file
- * SNMP thread synchronization implementation.
- */
-
-/*
- * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * Author: Dirk Ziegelmeier <dziegel@gmx.de>
- */
-
-#include "lwip/apps/snmp_opts.h"
-
-#if LWIP_SNMP && (NO_SYS == 0) /* don't build if not configured for use in lwipopts.h */
-
-#include "lwip/apps/snmp_threadsync.h"
-#include "lwip/apps/snmp_core.h"
-#include "lwip/sys.h"
-#include <string.h>
-
-static void
-call_synced_function(struct threadsync_data *call_data, snmp_threadsync_called_fn fn)
-{
-  sys_mutex_lock(&call_data->threadsync_node->instance->sem_usage_mutex);
-  call_data->threadsync_node->instance->sync_fn(fn, call_data);
-  sys_sem_wait(&call_data->threadsync_node->instance->sem);
-  sys_mutex_unlock(&call_data->threadsync_node->instance->sem_usage_mutex);
-}
-
-static void
-threadsync_get_value_synced(void *ctx)
-{
-  struct threadsync_data *call_data = (struct threadsync_data *)ctx;
-
-  if (call_data->proxy_instance.get_value != NULL) {
-    call_data->retval.s16 = call_data->proxy_instance.get_value(&call_data->proxy_instance, call_data->arg1.value);
-  } else {
-    call_data->retval.s16 = -1;
-  }
-
-  sys_sem_signal(&call_data->threadsync_node->instance->sem);
-}
-
-static s16_t
-threadsync_get_value(struct snmp_node_instance *instance, void *value)
-{
-  struct threadsync_data *call_data = (struct threadsync_data *)instance->reference.ptr;
-
-  call_data->arg1.value = value;
-  call_synced_function(call_data, threadsync_get_value_synced);
-
-  return call_data->retval.s16;
-}
-
-static void
-threadsync_set_test_synced(void *ctx)
-{
-  struct threadsync_data *call_data = (struct threadsync_data *)ctx;
-
-  if (call_data->proxy_instance.set_test != NULL) {
-    call_data->retval.err = call_data->proxy_instance.set_test(&call_data->proxy_instance, call_data->arg2.len, call_data->arg1.value);
-  } else {
-    call_data->retval.err = SNMP_ERR_NOTWRITABLE;
-  }
-
-  sys_sem_signal(&call_data->threadsync_node->instance->sem);
-}
-
-static snmp_err_t
-threadsync_set_test(struct snmp_node_instance *instance, u16_t len, void *value)
-{
-  struct threadsync_data *call_data = (struct threadsync_data *)instance->reference.ptr;
-
-  call_data->arg1.value = value;
-  call_data->arg2.len = len;
-  call_synced_function(call_data, threadsync_set_test_synced);
-
-  return call_data->retval.err;
-}
-
-static void
-threadsync_set_value_synced(void *ctx)
-{
-  struct threadsync_data *call_data = (struct threadsync_data *)ctx;
-
-  if (call_data->proxy_instance.set_value != NULL) {
-    call_data->retval.err = call_data->proxy_instance.set_value(&call_data->proxy_instance, call_data->arg2.len, call_data->arg1.value);
-  } else {
-    call_data->retval.err = SNMP_ERR_NOTWRITABLE;
-  }
-
-  sys_sem_signal(&call_data->threadsync_node->instance->sem);
-}
-
-static snmp_err_t
-threadsync_set_value(struct snmp_node_instance *instance, u16_t len, void *value)
-{
-  struct threadsync_data *call_data = (struct threadsync_data *)instance->reference.ptr;
-
-  call_data->arg1.value = value;
-  call_data->arg2.len = len;
-  call_synced_function(call_data, threadsync_set_value_synced);
-
-  return call_data->retval.err;
-}
-
-static void
-threadsync_release_instance_synced(void *ctx)
-{
-  struct threadsync_data *call_data = (struct threadsync_data *)ctx;
-
-  call_data->proxy_instance.release_instance(&call_data->proxy_instance);
-
-  sys_sem_signal(&call_data->threadsync_node->instance->sem);
-}
-
-static void
-threadsync_release_instance(struct snmp_node_instance *instance)
-{
-  struct threadsync_data *call_data = (struct threadsync_data *)instance->reference.ptr;
-
-  if (call_data->proxy_instance.release_instance != NULL) {
-    call_synced_function(call_data, threadsync_release_instance_synced);
-  }
-}
-
-static void
-get_instance_synced(void *ctx)
-{
-  struct threadsync_data *call_data   = (struct threadsync_data *)ctx;
-  const struct snmp_leaf_node *leaf   = (const struct snmp_leaf_node *)(const void *)call_data->proxy_instance.node;
-
-  call_data->retval.err = leaf->get_instance(call_data->arg1.root_oid, call_data->arg2.root_oid_len, &call_data->proxy_instance);
-
-  sys_sem_signal(&call_data->threadsync_node->instance->sem);
-}
-
-static void
-get_next_instance_synced(void *ctx)
-{
-  struct threadsync_data *call_data   = (struct threadsync_data *)ctx;
-  const struct snmp_leaf_node *leaf   = (const struct snmp_leaf_node *)(const void *)call_data->proxy_instance.node;
-
-  call_data->retval.err = leaf->get_next_instance(call_data->arg1.root_oid, call_data->arg2.root_oid_len, &call_data->proxy_instance);
-
-  sys_sem_signal(&call_data->threadsync_node->instance->sem);
-}
-
-static snmp_err_t
-do_sync(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance *instance, snmp_threadsync_called_fn fn)
-{
-  const struct snmp_threadsync_node *threadsync_node = (const struct snmp_threadsync_node *)(const void *)instance->node;
-  struct threadsync_data *call_data = &threadsync_node->instance->data;
-
-  if (threadsync_node->node.node.oid != threadsync_node->target->node.oid) {
-    LWIP_DEBUGF(SNMP_DEBUG, ("Sync node OID does not match target node OID"));
-    return SNMP_ERR_NOSUCHINSTANCE;
-  }
-
-  memset(&call_data->proxy_instance, 0, sizeof(call_data->proxy_instance));
-
-  instance->reference.ptr = call_data;
-  snmp_oid_assign(&call_data->proxy_instance.instance_oid, instance->instance_oid.id, instance->instance_oid.len);
-
-  call_data->proxy_instance.node = &threadsync_node->target->node;
-  call_data->threadsync_node     = threadsync_node;
-
-  call_data->arg1.root_oid       = root_oid;
-  call_data->arg2.root_oid_len   = root_oid_len;
-  call_synced_function(call_data, fn);
-
-  if (call_data->retval.err == SNMP_ERR_NOERROR) {
-    instance->access           = call_data->proxy_instance.access;
-    instance->asn1_type        = call_data->proxy_instance.asn1_type;
-    instance->release_instance = threadsync_release_instance;
-    instance->get_value        = (call_data->proxy_instance.get_value != NULL) ? threadsync_get_value : NULL;
-    instance->set_value        = (call_data->proxy_instance.set_value != NULL) ? threadsync_set_value : NULL;
-    instance->set_test         = (call_data->proxy_instance.set_test != NULL) ?  threadsync_set_test  : NULL;
-    snmp_oid_assign(&instance->instance_oid, call_data->proxy_instance.instance_oid.id, call_data->proxy_instance.instance_oid.len);
-  }
-
-  return call_data->retval.err;
-}
-
-snmp_err_t
-snmp_threadsync_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance *instance)
-{
-  return do_sync(root_oid, root_oid_len, instance, get_instance_synced);
-}
-
-snmp_err_t
-snmp_threadsync_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance *instance)
-{
-  return do_sync(root_oid, root_oid_len, instance, get_next_instance_synced);
-}
-
-/** Initializes thread synchronization instance */
-void snmp_threadsync_init(struct snmp_threadsync_instance *instance, snmp_threadsync_synchronizer_fn sync_fn)
-{
-  err_t err = sys_mutex_new(&instance->sem_usage_mutex);
-  LWIP_ASSERT("Failed to set up mutex", err == ERR_OK);
-  err = sys_sem_new(&instance->sem, 0);
-  LWIP_UNUSED_ARG(err); /* in case of LWIP_NOASSERT */
-  LWIP_ASSERT("Failed to set up semaphore", err == ERR_OK);
-  instance->sync_fn = sync_fn;
-}
-
-#endif /* LWIP_SNMP */
+/**
+ * @file
+ * SNMP thread synchronization implementation.
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Dirk Ziegelmeier <dziegel@gmx.de>
+ */
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP && (NO_SYS == 0) /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/apps/snmp_threadsync.h"
+#include "lwip/apps/snmp_core.h"
+#include "lwip/sys.h"
+#include <string.h>
+/*
+static void
+call_synced_function(struct threadsync_data *call_data, snmp_threadsync_called_fn fn)
+{
+  sys_mutex_lock(&call_data->threadsync_node->instance->sem_usage_mutex);
+  call_data->threadsync_node->instance->sync_fn(fn, call_data);
+  sys_sem_wait(&call_data->threadsync_node->instance->sem);
+  sys_mutex_unlock(&call_data->threadsync_node->instance->sem_usage_mutex);
+}
+
+static void
+threadsync_get_value_synced(void *ctx)
+{
+  struct threadsync_data *call_data = (struct threadsync_data*)ctx;
+
+  call_data->retval.s16 = call_data->proxy_instance.get_value(&call_data->proxy_instance, call_data->arg1.value);
+
+  sys_sem_signal(&call_data->threadsync_node->instance->sem);
+}
+
+static s16_t
+threadsync_get_value(struct snmp_node_instance* instance, void* value)
+{
+  struct threadsync_data *call_data = (struct threadsync_data*)instance->reference.ptr;
+
+  call_data->arg1.value = value;
+  call_synced_function(call_data, threadsync_get_value_synced);
+
+  return call_data->retval.s16;
+}
+
+static void
+threadsync_set_test_synced(void *ctx)
+{
+  struct threadsync_data *call_data = (struct threadsync_data*)ctx;
+
+  call_data->retval.err = call_data->proxy_instance.set_test(&call_data->proxy_instance, call_data->arg2.len, call_data->arg1.value);
+
+  sys_sem_signal(&call_data->threadsync_node->instance->sem);
+}
+
+static snmp_err_t
+threadsync_set_test(struct snmp_node_instance* instance, u16_t len, void *value)
+{
+  struct threadsync_data *call_data = (struct threadsync_data*)instance->reference.ptr;
+
+  call_data->arg1.value = value;
+  call_data->arg2.len = len;
+  call_synced_function(call_data, threadsync_set_test_synced);
+
+  return call_data->retval.err;
+}
+
+static void
+threadsync_set_value_synced(void *ctx)
+{
+  struct threadsync_data *call_data = (struct threadsync_data*)ctx;
+
+  call_data->retval.err = call_data->proxy_instance.set_value(&call_data->proxy_instance, call_data->arg2.len, call_data->arg1.value);
+
+  sys_sem_signal(&call_data->threadsync_node->instance->sem);
+}
+
+static snmp_err_t
+threadsync_set_value(struct snmp_node_instance* instance, u16_t len, void *value)
+{
+  struct threadsync_data *call_data = (struct threadsync_data*)instance->reference.ptr;
+
+  call_data->arg1.value = value;
+  call_data->arg2.len = len;
+  call_synced_function(call_data, threadsync_set_value_synced);
+  
+  return call_data->retval.err;
+}
+
+static void
+threadsync_release_instance_synced(void* ctx)
+{
+  struct threadsync_data *call_data = (struct threadsync_data*)ctx;
+  
+  call_data->proxy_instance.release_instance(&call_data->proxy_instance);
+
+  sys_sem_signal(&call_data->threadsync_node->instance->sem);
+}
+
+static void
+threadsync_release_instance(struct snmp_node_instance *instance)
+{
+  struct threadsync_data *call_data = (struct threadsync_data*)instance->reference.ptr;
+  
+  if (call_data->proxy_instance.release_instance != NULL) {
+    call_synced_function(call_data, threadsync_release_instance_synced);
+  }
+}
+
+static void
+get_instance_synced(void* ctx)
+{
+  struct threadsync_data *call_data   = (struct threadsync_data*)ctx;
+  const struct snmp_leaf_node *leaf   = (const struct snmp_leaf_node*)(const void*)call_data->proxy_instance.node;
+
+  call_data->retval.err = leaf->get_instance(call_data->arg1.root_oid, call_data->arg2.root_oid_len, &call_data->proxy_instance);
+
+  sys_sem_signal(&call_data->threadsync_node->instance->sem);
+}
+
+static void
+get_next_instance_synced(void* ctx)
+{
+  struct threadsync_data *call_data   = (struct threadsync_data*)ctx;
+  const struct snmp_leaf_node *leaf   = (const struct snmp_leaf_node*)(const void*)call_data->proxy_instance.node;
+
+  call_data->retval.err = leaf->get_next_instance(call_data->arg1.root_oid, call_data->arg2.root_oid_len, &call_data->proxy_instance);
+
+  sys_sem_signal(&call_data->threadsync_node->instance->sem);
+}
+
+static snmp_err_t
+do_sync(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance, snmp_threadsync_called_fn fn)
+{
+  const struct snmp_threadsync_node *threadsync_node = (const struct snmp_threadsync_node*)(const void*)instance->node;
+  struct threadsync_data *call_data = &threadsync_node->instance->data;
+
+  if (threadsync_node->node.node.oid != threadsync_node->target->node.oid) {
+    LWIP_DEBUGF(SNMP_DEBUG, ("Sync node OID does not match target node OID"));
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  memset(&call_data->proxy_instance, 0, sizeof(call_data->proxy_instance));
+
+  instance->reference.ptr = call_data;
+  snmp_oid_assign(&call_data->proxy_instance.instance_oid, instance->instance_oid.id, instance->instance_oid.len);
+
+  call_data->proxy_instance.node = &threadsync_node->target->node;
+  call_data->threadsync_node     = threadsync_node;
+
+  call_data->arg1.root_oid       = root_oid;
+  call_data->arg2.root_oid_len   = root_oid_len;
+  call_synced_function(call_data, fn);
+
+  if (call_data->retval.err == SNMP_ERR_NOERROR) {
+    instance->access           = call_data->proxy_instance.access;
+    instance->asn1_type        = call_data->proxy_instance.asn1_type;
+    instance->release_instance = threadsync_release_instance;
+    instance->get_value        = (call_data->proxy_instance.get_value != NULL)? threadsync_get_value : NULL;
+    instance->set_value        = (call_data->proxy_instance.set_value != NULL)? threadsync_set_value : NULL;
+    instance->set_test         = (call_data->proxy_instance.set_test != NULL)?  threadsync_set_test  : NULL;
+    snmp_oid_assign(&instance->instance_oid, call_data->proxy_instance.instance_oid.id, call_data->proxy_instance.instance_oid.len);
+  }
+
+  return call_data->retval.err;
+}
+*/
+
+snmp_err_t
+snmp_threadsync_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance)
+{
+  (void)root_oid; (void)root_oid_len; (void)instance;
+  //return do_sync(root_oid, root_oid_len, instance, get_instance_synced);
+  return SNMP_ERR_NOACCESS;
+}
+
+snmp_err_t
+snmp_threadsync_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance)
+{
+  (void)root_oid; (void)root_oid_len; (void)instance;
+  //return do_sync(root_oid, root_oid_len, instance, get_next_instance_synced);
+  return SNMP_ERR_NOACCESS;
+}
+
+/** Initializes thread synchronization instance */
+void snmp_threadsync_init(struct snmp_threadsync_instance *instance, snmp_threadsync_synchronizer_fn sync_fn)
+{
+  err_t err = sys_mutex_new(&instance->sem_usage_mutex);
+  LWIP_ASSERT("Failed to set up mutex", err == ERR_OK);
+  err = sys_sem_new(&instance->sem, 0);
+  LWIP_UNUSED_ARG(err); /* in case of LWIP_NOASSERT */
+  LWIP_ASSERT("Failed to set up semaphore", err == ERR_OK);
+  instance->sync_fn = sync_fn;
+}
+
+#endif /* LWIP_SNMP */

+ 447 - 458
libs/thirdparty/lwip_2.1.2/src/apps/snmp/snmp_traps.c → libs/thirdparty/LwIP/src/apps/snmp/snmp_traps.c

@@ -1,458 +1,447 @@
-/**
- * @file
- * SNMPv1 traps implementation.
- */
-
-/*
- * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- *
- * Author: Martin Hentschel
- *         Christiaan Simons <christiaan.simons@axon.tv>
- *
- */
-
-#include "lwip/apps/snmp_opts.h"
-
-#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
-
-#include <string.h>
-
-#include "lwip/snmp.h"
-#include "lwip/sys.h"
-#include "lwip/apps/snmp.h"
-#include "lwip/apps/snmp_core.h"
-#include "lwip/prot/iana.h"
-#include "snmp_msg.h"
-#include "snmp_asn1.h"
-#include "snmp_core_priv.h"
-
-struct snmp_msg_trap {
-  /* source enterprise ID (sysObjectID) */
-  const struct snmp_obj_id *enterprise;
-  /* source IP address, raw network order format */
-  ip_addr_t sip;
-  /* generic trap code */
-  u32_t gen_trap;
-  /* specific trap code */
-  u32_t spc_trap;
-  /* timestamp */
-  u32_t ts;
-  /* snmp_version */
-  u32_t snmp_version;
-
-  /* output trap lengths used in ASN encoding */
-  /* encoding pdu length */
-  u16_t pdulen;
-  /* encoding community length */
-  u16_t comlen;
-  /* encoding sequence length */
-  u16_t seqlen;
-  /* encoding varbinds sequence length */
-  u16_t vbseqlen;
-};
-
-static u16_t snmp_trap_varbind_sum(struct snmp_msg_trap *trap, struct snmp_varbind *varbinds);
-static u16_t snmp_trap_header_sum(struct snmp_msg_trap *trap, u16_t vb_len);
-static err_t snmp_trap_header_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream);
-static err_t snmp_trap_varbind_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream, struct snmp_varbind *varbinds);
-
-#define BUILD_EXEC(code) \
-  if ((code) != ERR_OK) { \
-    LWIP_DEBUGF(SNMP_DEBUG, ("SNMP error during creation of outbound trap frame!")); \
-    return ERR_ARG; \
-  }
-
-/** Agent community string for sending traps */
-extern const char *snmp_community_trap;
-
-void *snmp_traps_handle;
-
-struct snmp_trap_dst {
-  /* destination IP address in network order */
-  ip_addr_t dip;
-  /* set to 0 when disabled, >0 when enabled */
-  u8_t enable;
-};
-static struct snmp_trap_dst trap_dst[SNMP_TRAP_DESTINATIONS];
-
-static u8_t snmp_auth_traps_enabled = 0;
-
-/**
- * @ingroup snmp_traps
- * Sets enable switch for this trap destination.
- * @param dst_idx index in 0 .. SNMP_TRAP_DESTINATIONS-1
- * @param enable switch if 0 destination is disabled >0 enabled.
- */
-void
-snmp_trap_dst_enable(u8_t dst_idx, u8_t enable)
-{
-  LWIP_ASSERT_CORE_LOCKED();
-  if (dst_idx < SNMP_TRAP_DESTINATIONS) {
-    trap_dst[dst_idx].enable = enable;
-  }
-}
-
-/**
- * @ingroup snmp_traps
- * Sets IPv4 address for this trap destination.
- * @param dst_idx index in 0 .. SNMP_TRAP_DESTINATIONS-1
- * @param dst IPv4 address in host order.
- */
-void
-snmp_trap_dst_ip_set(u8_t dst_idx, const ip_addr_t *dst)
-{
-  LWIP_ASSERT_CORE_LOCKED();
-  if (dst_idx < SNMP_TRAP_DESTINATIONS) {
-    ip_addr_set(&trap_dst[dst_idx].dip, dst);
-  }
-}
-
-/**
- * @ingroup snmp_traps
- * Enable/disable authentication traps
- */
-void
-snmp_set_auth_traps_enabled(u8_t enable)
-{
-  snmp_auth_traps_enabled = enable;
-}
-
-/**
- * @ingroup snmp_traps
- * Get authentication traps enabled state
- */
-u8_t
-snmp_get_auth_traps_enabled(void)
-{
-  return snmp_auth_traps_enabled;
-}
-
-
-/**
- * @ingroup snmp_traps
- * Sends a generic or enterprise specific trap message.
- *
- * @param eoid points to enterprise object identifier
- * @param generic_trap is the trap code
- * @param specific_trap used for enterprise traps when generic_trap == 6
- * @param varbinds linked list of varbinds to be sent
- * @return ERR_OK when success, ERR_MEM if we're out of memory
- *
- * @note the use of the enterprise identifier field
- * is per RFC1215.
- * Use .iso.org.dod.internet.mgmt.mib-2.snmp for generic traps
- * and .iso.org.dod.internet.private.enterprises.yourenterprise
- * (sysObjectID) for specific traps.
- */
-err_t
-snmp_send_trap(const struct snmp_obj_id *eoid, s32_t generic_trap, s32_t specific_trap, struct snmp_varbind *varbinds)
-{
-  struct snmp_msg_trap trap_msg;
-  struct snmp_trap_dst *td;
-  struct pbuf *p;
-  u16_t i, tot_len;
-  err_t err = ERR_OK;
-
-  LWIP_ASSERT_CORE_LOCKED();
-
-  trap_msg.snmp_version = 0;
-
-  for (i = 0, td = &trap_dst[0]; i < SNMP_TRAP_DESTINATIONS; i++, td++) {
-    if ((td->enable != 0) && !ip_addr_isany(&td->dip)) {
-      /* lookup current source address for this dst */
-      if (snmp_get_local_ip_for_dst(snmp_traps_handle, &td->dip, &trap_msg.sip)) {
-        if (eoid == NULL) {
-          trap_msg.enterprise = snmp_get_device_enterprise_oid();
-        } else {
-          trap_msg.enterprise = eoid;
-        }
-
-        trap_msg.gen_trap = generic_trap;
-        if (generic_trap == SNMP_GENTRAP_ENTERPRISE_SPECIFIC) {
-          trap_msg.spc_trap = specific_trap;
-        } else {
-          trap_msg.spc_trap = 0;
-        }
-
-        MIB2_COPY_SYSUPTIME_TO(&trap_msg.ts);
-
-        /* pass 0, calculate length fields */
-        tot_len = snmp_trap_varbind_sum(&trap_msg, varbinds);
-        tot_len = snmp_trap_header_sum(&trap_msg, tot_len);
-
-        /* allocate pbuf(s) */
-        p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_RAM);
-        if (p != NULL) {
-          struct snmp_pbuf_stream pbuf_stream;
-          snmp_pbuf_stream_init(&pbuf_stream, p, 0, tot_len);
-
-          /* pass 1, encode packet into the pbuf(s) */
-          snmp_trap_header_enc(&trap_msg, &pbuf_stream);
-          snmp_trap_varbind_enc(&trap_msg, &pbuf_stream, varbinds);
-
-          snmp_stats.outtraps++;
-          snmp_stats.outpkts++;
-
-          /** send to the TRAP destination */
-          snmp_sendto(snmp_traps_handle, p, &td->dip, LWIP_IANA_PORT_SNMP_TRAP);
-          pbuf_free(p);
-        } else {
-          err = ERR_MEM;
-        }
-      } else {
-        /* routing error */
-        err = ERR_RTE;
-      }
-    }
-  }
-  return err;
-}
-
-/**
- * @ingroup snmp_traps
- * Send generic SNMP trap
- */
-err_t
-snmp_send_trap_generic(s32_t generic_trap)
-{
-  static const struct snmp_obj_id oid = { 7, { 1, 3, 6, 1, 2, 1, 11 } };
-  return snmp_send_trap(&oid, generic_trap, 0, NULL);
-}
-
-/**
- * @ingroup snmp_traps
- * Send specific SNMP trap with variable bindings
- */
-err_t
-snmp_send_trap_specific(s32_t specific_trap, struct snmp_varbind *varbinds)
-{
-  return snmp_send_trap(NULL, SNMP_GENTRAP_ENTERPRISE_SPECIFIC, specific_trap, varbinds);
-}
-
-/**
- * @ingroup snmp_traps
- * Send coldstart trap
- */
-void
-snmp_coldstart_trap(void)
-{
-  snmp_send_trap_generic(SNMP_GENTRAP_COLDSTART);
-}
-
-/**
- * @ingroup snmp_traps
- * Send authentication failure trap (used internally by agent)
- */
-void
-snmp_authfail_trap(void)
-{
-  if (snmp_auth_traps_enabled != 0) {
-    snmp_send_trap_generic(SNMP_GENTRAP_AUTH_FAILURE);
-  }
-}
-
-static u16_t
-snmp_trap_varbind_sum(struct snmp_msg_trap *trap, struct snmp_varbind *varbinds)
-{
-  struct snmp_varbind *varbind;
-  u16_t tot_len;
-  u8_t tot_len_len;
-
-  tot_len = 0;
-  varbind = varbinds;
-  while (varbind != NULL) {
-    struct snmp_varbind_len len;
-
-    if (snmp_varbind_length(varbind, &len) == ERR_OK) {
-      tot_len += 1 + len.vb_len_len + len.vb_value_len;
-    }
-
-    varbind = varbind->next;
-  }
-
-  trap->vbseqlen = tot_len;
-  snmp_asn1_enc_length_cnt(trap->vbseqlen, &tot_len_len);
-  tot_len += 1 + tot_len_len;
-
-  return tot_len;
-}
-
-/**
- * Sums trap header field lengths from tail to head and
- * returns trap_header_lengths for second encoding pass.
- *
- * @param trap Trap message
- * @param vb_len varbind-list length
- * @return the required length for encoding the trap header
- */
-static u16_t
-snmp_trap_header_sum(struct snmp_msg_trap *trap, u16_t vb_len)
-{
-  u16_t tot_len;
-  u16_t len;
-  u8_t lenlen;
-
-  tot_len = vb_len;
-
-  snmp_asn1_enc_u32t_cnt(trap->ts, &len);
-  snmp_asn1_enc_length_cnt(len, &lenlen);
-  tot_len += 1 + len + lenlen;
-
-  snmp_asn1_enc_s32t_cnt(trap->spc_trap, &len);
-  snmp_asn1_enc_length_cnt(len, &lenlen);
-  tot_len += 1 + len + lenlen;
-
-  snmp_asn1_enc_s32t_cnt(trap->gen_trap, &len);
-  snmp_asn1_enc_length_cnt(len, &lenlen);
-  tot_len += 1 + len + lenlen;
-
-  if (IP_IS_V6_VAL(trap->sip)) {
-#if LWIP_IPV6
-    len = sizeof(ip_2_ip6(&trap->sip)->addr);
-#endif
-  } else {
-#if LWIP_IPV4
-    len = sizeof(ip_2_ip4(&trap->sip)->addr);
-#endif
-  }
-  snmp_asn1_enc_length_cnt(len, &lenlen);
-  tot_len += 1 + len + lenlen;
-
-  snmp_asn1_enc_oid_cnt(trap->enterprise->id, trap->enterprise->len, &len);
-  snmp_asn1_enc_length_cnt(len, &lenlen);
-  tot_len += 1 + len + lenlen;
-
-  trap->pdulen = tot_len;
-  snmp_asn1_enc_length_cnt(trap->pdulen, &lenlen);
-  tot_len += 1 + lenlen;
-
-  trap->comlen = (u16_t)LWIP_MIN(strlen(snmp_community_trap), 0xFFFF);
-  snmp_asn1_enc_length_cnt(trap->comlen, &lenlen);
-  tot_len += 1 + lenlen + trap->comlen;
-
-  snmp_asn1_enc_s32t_cnt(trap->snmp_version, &len);
-  snmp_asn1_enc_length_cnt(len, &lenlen);
-  tot_len += 1 + len + lenlen;
-
-  trap->seqlen = tot_len;
-  snmp_asn1_enc_length_cnt(trap->seqlen, &lenlen);
-  tot_len += 1 + lenlen;
-
-  return tot_len;
-}
-
-static err_t
-snmp_trap_varbind_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream, struct snmp_varbind *varbinds)
-{
-  struct snmp_asn1_tlv tlv;
-  struct snmp_varbind *varbind;
-
-  varbind = varbinds;
-
-  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 0, trap->vbseqlen);
-  BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
-
-  while (varbind != NULL) {
-    BUILD_EXEC( snmp_append_outbound_varbind(pbuf_stream, varbind) );
-
-    varbind = varbind->next;
-  }
-
-  return ERR_OK;
-}
-
-/**
- * Encodes trap header from head to tail.
- */
-static err_t
-snmp_trap_header_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream)
-{
-  struct snmp_asn1_tlv tlv;
-
-  /* 'Message' sequence */
-  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 0, trap->seqlen);
-  BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
-
-  /* version */
-  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
-  snmp_asn1_enc_s32t_cnt(trap->snmp_version, &tlv.value_len);
-  BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
-  BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->snmp_version) );
-
-  /* community */
-  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, trap->comlen);
-  BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
-  BUILD_EXEC( snmp_asn1_enc_raw(pbuf_stream,  (const u8_t *)snmp_community_trap, trap->comlen) );
-
-  /* 'PDU' sequence */
-  SNMP_ASN1_SET_TLV_PARAMS(tlv, (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_TRAP), 0, trap->pdulen);
-  BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
-
-  /* object ID */
-  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OBJECT_ID, 0, 0);
-  snmp_asn1_enc_oid_cnt(trap->enterprise->id, trap->enterprise->len, &tlv.value_len);
-  BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
-  BUILD_EXEC( snmp_asn1_enc_oid(pbuf_stream, trap->enterprise->id, trap->enterprise->len) );
-
-  /* IP addr */
-  if (IP_IS_V6_VAL(trap->sip)) {
-#if LWIP_IPV6
-    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_IPADDR, 0, sizeof(ip_2_ip6(&trap->sip)->addr));
-    BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
-    BUILD_EXEC( snmp_asn1_enc_raw(pbuf_stream, (const u8_t *)&ip_2_ip6(&trap->sip)->addr, sizeof(ip_2_ip6(&trap->sip)->addr)) );
-#endif
-  } else {
-#if LWIP_IPV4
-    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_IPADDR, 0, sizeof(ip_2_ip4(&trap->sip)->addr));
-    BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
-    BUILD_EXEC( snmp_asn1_enc_raw(pbuf_stream, (const u8_t *)&ip_2_ip4(&trap->sip)->addr, sizeof(ip_2_ip4(&trap->sip)->addr)) );
-#endif
-  }
-
-  /* trap length */
-  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
-  snmp_asn1_enc_s32t_cnt(trap->gen_trap, &tlv.value_len);
-  BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
-  BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->gen_trap) );
-
-  /* specific trap */
-  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
-  snmp_asn1_enc_s32t_cnt(trap->spc_trap, &tlv.value_len);
-  BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
-  BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->spc_trap) );
-
-  /* timestamp */
-  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_TIMETICKS, 0, 0);
-  snmp_asn1_enc_s32t_cnt(trap->ts, &tlv.value_len);
-  BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
-  BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->ts) );
-
-  return ERR_OK;
-}
-
-#endif /* LWIP_SNMP */
+/**
+ * @file
+ * SNMPv1 traps implementation.
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Martin Hentschel
+ *         Christiaan Simons <christiaan.simons@axon.tv>
+ *
+ */
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include <string.h>
+
+#include "lwip/snmp.h"
+#include "lwip/sys.h"
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "snmp_msg.h"
+#include "snmp_asn1.h"
+#include "snmp_core_priv.h"
+
+struct snmp_msg_trap
+{
+  /* source enterprise ID (sysObjectID) */
+  const struct snmp_obj_id *enterprise;
+  /* source IP address, raw network order format */
+  ip_addr_t sip;
+  /* generic trap code */
+  u32_t gen_trap;
+  /* specific trap code */
+  u32_t spc_trap;
+  /* timestamp */
+  u32_t ts;
+  /* snmp_version */
+  u32_t snmp_version;
+
+  /* output trap lengths used in ASN encoding */
+  /* encoding pdu length */
+  u16_t pdulen;
+  /* encoding community length */
+  u16_t comlen;
+  /* encoding sequence length */
+  u16_t seqlen;
+  /* encoding varbinds sequence length */
+  u16_t vbseqlen;
+};
+
+static u16_t snmp_trap_varbind_sum(struct snmp_msg_trap *trap, struct snmp_varbind *varbinds);
+static u16_t snmp_trap_header_sum(struct snmp_msg_trap *trap, u16_t vb_len);
+static void snmp_trap_header_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream);
+static void snmp_trap_varbind_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream, struct snmp_varbind *varbinds);
+
+/** Agent community string for sending traps */
+extern const char *snmp_community_trap;
+
+void* snmp_traps_handle;
+
+struct snmp_trap_dst
+{
+  /* destination IP address in network order */
+  ip_addr_t dip;
+  /* set to 0 when disabled, >0 when enabled */
+  u8_t enable;
+};
+static struct snmp_trap_dst trap_dst[SNMP_TRAP_DESTINATIONS];
+
+static u8_t snmp_auth_traps_enabled = 0;
+
+/**
+ * @ingroup snmp_traps
+ * Sets enable switch for this trap destination.
+ * @param dst_idx index in 0 .. SNMP_TRAP_DESTINATIONS-1
+ * @param enable switch if 0 destination is disabled >0 enabled.
+ */
+void
+snmp_trap_dst_enable(u8_t dst_idx, u8_t enable)
+{
+  if (dst_idx < SNMP_TRAP_DESTINATIONS) {
+    trap_dst[dst_idx].enable = enable;
+  }
+}
+
+/**
+ * @ingroup snmp_traps
+ * Sets IPv4 address for this trap destination.
+ * @param dst_idx index in 0 .. SNMP_TRAP_DESTINATIONS-1
+ * @param dst IPv4 address in host order.
+ */
+void
+snmp_trap_dst_ip_set(u8_t dst_idx, const ip_addr_t *dst)
+{
+  if (dst_idx < SNMP_TRAP_DESTINATIONS) {
+    ip_addr_set(&trap_dst[dst_idx].dip, dst);
+  }
+}
+
+/**
+ * @ingroup snmp_traps
+ * Enable/disable authentication traps
+ */
+void
+snmp_set_auth_traps_enabled(u8_t enable)
+{
+  snmp_auth_traps_enabled = enable;
+}
+
+/**
+ * @ingroup snmp_traps
+ * Get authentication traps enabled state
+ */
+u8_t
+snmp_get_auth_traps_enabled(void)
+{
+  return snmp_auth_traps_enabled;
+}
+
+
+/**
+ * @ingroup snmp_traps
+ * Sends a generic or enterprise specific trap message.
+ *
+ * @param eoid points to enterprise object identifier
+ * @param generic_trap is the trap code
+ * @param specific_trap used for enterprise traps when generic_trap == 6
+ * @param varbinds linked list of varbinds to be sent
+ * @return ERR_OK when success, ERR_MEM if we're out of memory
+ *
+ * @note the use of the enterprise identifier field
+ * is per RFC1215.
+ * Use .iso.org.dod.internet.mgmt.mib-2.snmp for generic traps
+ * and .iso.org.dod.internet.private.enterprises.yourenterprise
+ * (sysObjectID) for specific traps.
+ */
+err_t
+snmp_send_trap(const struct snmp_obj_id* eoid, s32_t generic_trap, s32_t specific_trap, struct snmp_varbind *varbinds)
+{
+  struct snmp_msg_trap trap_msg;
+  struct snmp_trap_dst *td;
+  struct pbuf *p;
+  u16_t i, tot_len; 
+  err_t err = ERR_OK;
+
+  trap_msg.snmp_version = 1;
+  //trap_msg.snmp_version = 0x7900L;
+
+  for (i = 0, td = &trap_dst[0]; i < SNMP_TRAP_DESTINATIONS; i++, td++) {
+    if ((td->enable != 0) && !ip_addr_isany(&td->dip)) {
+      /* lookup current source address for this dst */
+      if (snmp_get_local_ip_for_dst(snmp_traps_handle, &td->dip, &trap_msg.sip)) {
+        if (eoid == NULL) {
+          trap_msg.enterprise = snmp_get_device_enterprise_oid();
+        } else {
+          trap_msg.enterprise = eoid;
+        }
+
+        trap_msg.gen_trap = generic_trap;
+        if (generic_trap == SNMP_GENTRAP_ENTERPRISE_SPECIFIC) {
+          trap_msg.spc_trap = specific_trap;
+        } else {
+          trap_msg.spc_trap = 0;
+        }
+
+        MIB2_COPY_SYSUPTIME_TO(&trap_msg.ts);
+
+        /* pass 0, calculate length fields */
+        tot_len = snmp_trap_varbind_sum(&trap_msg, varbinds);
+        tot_len = snmp_trap_header_sum(&trap_msg, tot_len);
+
+        /* allocate pbuf(s) */
+        p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_RAM);
+        if (p != NULL) {
+          struct snmp_pbuf_stream pbuf_stream;
+          snmp_pbuf_stream_init(&pbuf_stream, p, 0, tot_len);
+
+          /* pass 1, encode packet ino the pbuf(s) */
+          snmp_trap_header_enc(&trap_msg, &pbuf_stream);
+          snmp_trap_varbind_enc(&trap_msg, &pbuf_stream, varbinds);
+
+          snmp_stats.outtraps++;
+          snmp_stats.outpkts++;
+
+          /** send to the TRAP destination */
+          snmp_sendto(snmp_traps_handle, p, &td->dip, SNMP_TRAP_PORT);
+          pbuf_free(p);
+        } else {
+          err = ERR_MEM;
+        }
+      } else {
+        /* routing error */
+        err = ERR_RTE;
+      }
+    }
+  }
+  return err;
+}
+
+/**
+ * @ingroup snmp_traps
+ * Send generic SNMP trap
+ */
+err_t 
+snmp_send_trap_generic(s32_t generic_trap)
+{
+  //static const struct snmp_obj_id oid = { 7, { 1, 3, 6, 1, 2, 1, 11 } };
+  static const struct snmp_obj_id oid = { 10, { 1, 3, 6, 1, 4, 1, 41752, 911, 3, 2 } };
+  return snmp_send_trap(&oid, generic_trap, 0, NULL);
+}
+
+/**
+ * @ingroup snmp_traps
+ * Send specific SNMP trap with variable bindings
+ */
+err_t
+snmp_send_trap_specific(s32_t specific_trap, struct snmp_varbind *varbinds)
+{
+  return snmp_send_trap(NULL, SNMP_GENTRAP_ENTERPRISE_SPECIFIC, specific_trap, varbinds);
+}
+
+/**
+ * @ingroup snmp_traps
+ * Send coldstart trap
+ */
+void
+snmp_coldstart_trap(void)
+{
+  snmp_send_trap_generic(SNMP_GENTRAP_COLDSTART);
+}
+
+/**
+ * @ingroup snmp_traps
+ * Send authentication failure trap (used internally by agent) 
+ */
+void
+snmp_authfail_trap(void)
+{
+  if (snmp_auth_traps_enabled != 0) {
+    snmp_send_trap_generic(SNMP_GENTRAP_AUTH_FAILURE);
+  }
+}
+
+static u16_t
+snmp_trap_varbind_sum(struct snmp_msg_trap *trap, struct snmp_varbind *varbinds)
+{
+  struct snmp_varbind *varbind;
+  u16_t tot_len;
+  u8_t tot_len_len;
+
+  tot_len = 0;
+  varbind = varbinds;
+  while (varbind != NULL) {
+    struct snmp_varbind_len len;
+
+    if (snmp_varbind_length(varbind, &len) == ERR_OK) {
+      tot_len += 1 + len.vb_len_len + len.vb_value_len;
+    }
+
+    varbind = varbind->next;
+  }
+
+  trap->vbseqlen = tot_len;
+  snmp_asn1_enc_length_cnt(trap->vbseqlen, &tot_len_len);
+  tot_len += 1 + tot_len_len;
+
+  return tot_len;
+}
+
+/**
+ * Sums trap header field lengths from tail to head and
+ * returns trap_header_lengths for second encoding pass.
+ *
+ * @param trap Trap message
+ * @param vb_len varbind-list length
+ * @return the required length for encoding the trap header
+ */
+static u16_t
+snmp_trap_header_sum(struct snmp_msg_trap *trap, u16_t vb_len)
+{
+  u16_t tot_len;
+  u16_t len;
+  u8_t lenlen;
+
+  tot_len = vb_len;
+
+  snmp_asn1_enc_u32t_cnt(trap->ts, &len);
+  snmp_asn1_enc_length_cnt(len, &lenlen);
+  tot_len += 1 + len + lenlen;
+
+  snmp_asn1_enc_s32t_cnt(trap->spc_trap, &len);
+  snmp_asn1_enc_length_cnt(len, &lenlen);
+  tot_len += 1 + len + lenlen;
+
+  snmp_asn1_enc_s32t_cnt(trap->gen_trap, &len);
+  snmp_asn1_enc_length_cnt(len, &lenlen);
+  tot_len += 1 + len + lenlen;
+
+  if (IP_IS_V6_VAL(trap->sip)) {
+#if LWIP_IPV6
+    len = sizeof(ip_2_ip6(&trap->sip)->addr);
+#endif
+  } else {
+#if LWIP_IPV4
+    len = sizeof(ip_2_ip4(&trap->sip)->addr);
+#endif
+  }
+  snmp_asn1_enc_length_cnt(len, &lenlen);
+  tot_len += 1 + len + lenlen;
+
+  snmp_asn1_enc_oid_cnt(trap->enterprise->id, trap->enterprise->len, &len);
+  snmp_asn1_enc_length_cnt(len, &lenlen);
+  tot_len += 1 + len + lenlen;
+
+  trap->pdulen = tot_len;
+  snmp_asn1_enc_length_cnt(trap->pdulen, &lenlen);
+  tot_len += 1 + lenlen;
+
+  trap->comlen = (u16_t)LWIP_MIN(strlen(snmp_community_trap), 0xFFFF);
+  snmp_asn1_enc_length_cnt(trap->comlen, &lenlen);
+  tot_len += 1 + lenlen + trap->comlen;
+
+  snmp_asn1_enc_s32t_cnt(trap->snmp_version, &len);
+  snmp_asn1_enc_length_cnt(len, &lenlen);
+  tot_len += 1 + len + lenlen;
+
+  trap->seqlen = tot_len;
+  snmp_asn1_enc_length_cnt(trap->seqlen, &lenlen);
+  tot_len += 1 + lenlen;
+
+  return tot_len;
+}
+
+static void
+snmp_trap_varbind_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream, struct snmp_varbind *varbinds)
+{
+  struct snmp_asn1_tlv tlv;
+  struct snmp_varbind *varbind;
+
+  varbind = varbinds;
+
+  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 0, trap->vbseqlen);
+  snmp_ans1_enc_tlv(pbuf_stream, &tlv);
+
+  while (varbind != NULL) {
+    snmp_append_outbound_varbind(pbuf_stream, varbind);
+
+    varbind = varbind->next;
+  }
+}
+
+/**
+ * Encodes trap header from head to tail.
+ */
+static void
+snmp_trap_header_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream)
+{
+  struct snmp_asn1_tlv tlv;
+
+  /* 'Message' sequence */
+  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 0, trap->seqlen);
+  snmp_ans1_enc_tlv(pbuf_stream, &tlv);
+
+  /* version */
+  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
+  snmp_asn1_enc_s32t_cnt(trap->snmp_version, &tlv.value_len);
+  snmp_ans1_enc_tlv(pbuf_stream, &tlv);
+  snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->snmp_version);
+
+  /* community */
+  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, trap->comlen);
+  snmp_ans1_enc_tlv(pbuf_stream, &tlv);
+  snmp_asn1_enc_raw(pbuf_stream,  (const u8_t *)snmp_community_trap, trap->comlen);
+
+  /* 'PDU' sequence */
+  SNMP_ASN1_SET_TLV_PARAMS(tlv, (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_TRAP), 0, trap->pdulen);
+  snmp_ans1_enc_tlv(pbuf_stream, &tlv);
+
+  /* object ID */
+  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OBJECT_ID, 0, 0);
+  snmp_asn1_enc_oid_cnt(trap->enterprise->id, trap->enterprise->len, &tlv.value_len);
+  snmp_ans1_enc_tlv(pbuf_stream, &tlv);
+  snmp_asn1_enc_oid(pbuf_stream, trap->enterprise->id, trap->enterprise->len);
+
+  /* IP addr */
+  if (IP_IS_V6_VAL(trap->sip)) {
+#if LWIP_IPV6
+    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_IPADDR, 0, sizeof(ip_2_ip6(&trap->sip)->addr));
+    snmp_ans1_enc_tlv(pbuf_stream, &tlv);
+    snmp_asn1_enc_raw(pbuf_stream, (const u8_t *)&ip_2_ip6(&trap->sip)->addr, sizeof(ip_2_ip6(&trap->sip)->addr));
+#endif
+  } else {
+#if LWIP_IPV4
+    SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_IPADDR, 0, sizeof(ip_2_ip4(&trap->sip)->addr));
+    snmp_ans1_enc_tlv(pbuf_stream, &tlv);
+    snmp_asn1_enc_raw(pbuf_stream, (const u8_t *)&ip_2_ip4(&trap->sip)->addr, sizeof(ip_2_ip4(&trap->sip)->addr));
+#endif
+  }
+
+  /* trap length */
+  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
+  snmp_asn1_enc_s32t_cnt(trap->gen_trap, &tlv.value_len);
+  snmp_ans1_enc_tlv(pbuf_stream, &tlv);
+  snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->gen_trap);
+
+  /* specific trap */
+  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
+  snmp_asn1_enc_s32t_cnt(trap->spc_trap, &tlv.value_len);
+  snmp_ans1_enc_tlv(pbuf_stream, &tlv);
+  snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->spc_trap);
+
+  /* timestamp */
+  SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_TIMETICKS, 0, 0);
+  snmp_asn1_enc_s32t_cnt(trap->ts, &tlv.value_len);
+  snmp_ans1_enc_tlv(pbuf_stream, &tlv);
+  snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->ts);
+}
+
+#endif /* LWIP_SNMP */

+ 136 - 136
libs/thirdparty/lwip_2.1.2/src/apps/snmp/snmpv3.c → libs/thirdparty/LwIP/src/apps/snmp/snmpv3.c

@@ -1,136 +1,136 @@
-/**
- * @file
- * Additional SNMPv3 functionality RFC3414 and RFC3826.
- */
-
-/*
- * Copyright (c) 2016 Elias Oenal.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * Author: Elias Oenal <lwip@eliasoenal.com>
- */
-
-#include "snmpv3_priv.h"
-#include "lwip/apps/snmpv3.h"
-#include "lwip/sys.h"
-#include <string.h>
-
-#if LWIP_SNMP && LWIP_SNMP_V3
-
-#ifdef LWIP_SNMPV3_INCLUDE_ENGINE
-#include LWIP_SNMPV3_INCLUDE_ENGINE
-#endif
-
-#define SNMP_MAX_TIME_BOOT 2147483647UL
-
-/** Call this if engine has been changed. Has to reset boots, see below */
-void
-snmpv3_engine_id_changed(void)
-{
-  snmpv3_set_engine_boots(0);
-}
-
-/** According to RFC3414 2.2.2.
- *
- * The number of times that the SNMP engine has
- * (re-)initialized itself since snmpEngineID
- * was last configured.
- */
-s32_t
-snmpv3_get_engine_boots_internal(void)
-{
-  if (snmpv3_get_engine_boots() == 0 ||
-      snmpv3_get_engine_boots() < SNMP_MAX_TIME_BOOT) {
-    return snmpv3_get_engine_boots();
-  }
-
-  snmpv3_set_engine_boots(SNMP_MAX_TIME_BOOT);
-  return snmpv3_get_engine_boots();
-}
-
-/** RFC3414 2.2.2.
- *
- * Once the timer reaches 2147483647 it gets reset to zero and the
- * engine boot ups get incremented.
- */
-s32_t
-snmpv3_get_engine_time_internal(void)
-{
-  if (snmpv3_get_engine_time() >= SNMP_MAX_TIME_BOOT) {
-    snmpv3_reset_engine_time();
-
-    if (snmpv3_get_engine_boots() < SNMP_MAX_TIME_BOOT - 1) {
-      snmpv3_set_engine_boots(snmpv3_get_engine_boots() + 1);
-    } else {
-      snmpv3_set_engine_boots(SNMP_MAX_TIME_BOOT);
-    }
-  }
-
-  return snmpv3_get_engine_time();
-}
-
-#if LWIP_SNMP_V3_CRYPTO
-
-/* This function ignores the byte order suggestion in RFC3414
- * since it simply doesn't influence the effectiveness of an IV.
- *
- * Implementing RFC3826 priv param algorithm if LWIP_RAND is available.
- *
- * @todo: This is a potential thread safety issue.
- */
-err_t
-snmpv3_build_priv_param(u8_t *priv_param)
-{
-#ifdef LWIP_RAND /* Based on RFC3826 */
-  static u8_t init;
-  static u32_t priv1, priv2;
-
-  /* Lazy initialisation */
-  if (init == 0) {
-    init = 1;
-    priv1 = LWIP_RAND();
-    priv2 = LWIP_RAND();
-  }
-
-  SMEMCPY(&priv_param[0], &priv1, sizeof(priv1));
-  SMEMCPY(&priv_param[4], &priv2, sizeof(priv2));
-
-  /* Emulate 64bit increment */
-  priv1++;
-  if (!priv1) { /* Overflow */
-    priv2++;
-  }
-#else /* Based on RFC3414 */
-  static u32_t ctr;
-  u32_t boots = snmpv3_get_engine_boots_internal();
-  SMEMCPY(&priv_param[0], &boots, 4);
-  SMEMCPY(&priv_param[4], &ctr, 4);
-  ctr++;
-#endif
-  return ERR_OK;
-}
-#endif /* LWIP_SNMP_V3_CRYPTO */
-
-#endif
+/**
+ * @file
+ * Additional SNMPv3 functionality RFC3414 and RFC3826.
+ */
+
+/*
+ * Copyright (c) 2016 Elias Oenal.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Elias Oenal <lwip@eliasoenal.com>
+ */
+
+#include "snmpv3_priv.h"
+#include "lwip/apps/snmpv3.h"
+#include "lwip/sys.h"
+#include <string.h>
+
+#if LWIP_SNMP && LWIP_SNMP_V3
+
+#ifdef LWIP_SNMPV3_INCLUDE_ENGINE
+#include LWIP_SNMPV3_INCLUDE_ENGINE
+#endif
+
+#define SNMP_MAX_TIME_BOOT 2147483647UL
+
+/** Call this if engine has been changed. Has to reset boots, see below */
+void
+snmpv3_engine_id_changed(void)
+{
+  snmpv3_set_engine_boots(0);
+}
+
+/** According to RFC3414 2.2.2.
+ *
+ * The number of times that the SNMP engine has
+ * (re-)initialized itself since snmpEngineID
+ * was last configured.
+ */
+u32_t
+snmpv3_get_engine_boots_internal(void)
+{
+  if (snmpv3_get_engine_boots() == 0 ||
+      snmpv3_get_engine_boots() < SNMP_MAX_TIME_BOOT) {
+    return snmpv3_get_engine_boots();
+  }
+
+  snmpv3_set_engine_boots(SNMP_MAX_TIME_BOOT);
+  return snmpv3_get_engine_boots();
+}
+
+/** RFC3414 2.2.2.
+ *
+ * Once the timer reaches 2147483647 it gets reset to zero and the
+ * engine boot ups get incremented.
+ */
+u32_t
+snmpv3_get_engine_time_internal(void)
+{
+  if (snmpv3_get_engine_time() >= SNMP_MAX_TIME_BOOT) {
+    snmpv3_reset_engine_time();
+
+    if (snmpv3_get_engine_boots() < SNMP_MAX_TIME_BOOT - 1) {
+      snmpv3_set_engine_boots(snmpv3_get_engine_boots() + 1);
+    } else {
+      snmpv3_set_engine_boots(SNMP_MAX_TIME_BOOT);
+    }
+  }
+
+  return snmpv3_get_engine_time();
+}
+
+#if LWIP_SNMP_V3_CRYPTO
+
+/* This function ignores the byte order suggestion in RFC3414
+ * since it simply doesn't influence the effectiveness of an IV.
+ *
+ * Implementing RFC3826 priv param algorithm if LWIP_RAND is available.
+ *
+ * @todo: This is a potential thread safety issue.
+ */
+err_t
+snmpv3_build_priv_param(u8_t* priv_param)
+{
+#ifdef LWIP_RAND /* Based on RFC3826 */
+  static u8_t init;
+  static u32_t priv1, priv2;
+
+  /* Lazy initialisation */
+  if (init == 0) {
+    init = 1;
+    priv1 = LWIP_RAND();
+    priv2 = LWIP_RAND();
+  }
+
+  SMEMCPY(&priv_param[0], &priv1, sizeof(priv1));
+  SMEMCPY(&priv_param[4], &priv2, sizeof(priv2));
+
+  /* Emulate 64bit increment */
+  priv1++;
+  if (!priv1) { /* Overflow */
+    priv2++;
+  }
+#else /* Based on RFC3414 */
+  static u32_t ctr;
+  u32_t boots = LWIP_SNMPV3_GET_ENGINE_BOOTS();
+  SMEMCPY(&priv_param[0], &boots, 4);
+  SMEMCPY(&priv_param[4], &ctr, 4);
+  ctr++;
+#endif
+  return ERR_OK;
+}
+#endif /* LWIP_SNMP_V3_CRYPTO */
+
+#endif

+ 145 - 0
libs/thirdparty/LwIP/src/apps/snmp/snmpv3_dummy.c

@@ -0,0 +1,145 @@
+/**
+ * @file
+ * Dummy SNMPv3 functions.
+ */
+
+/*
+ * Copyright (c) 2016 Elias Oenal.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Elias Oenal <lwip@eliasoenal.com>
+ *         Dirk Ziegelmeier <dirk@ziegelmeier.net>
+ */
+
+#include "lwip/apps/snmpv3.h"
+#include "snmpv3_priv.h"
+#include <string.h>
+#include "lwip/err.h"
+
+#if LWIP_SNMP && LWIP_SNMP_V3
+
+/**
+ *  @param username is a pointer to a string.
+ * @param auth_algo is a pointer to u8_t. The implementation has to set this if user was found.
+ * @param auth_key is a pointer to a pointer to a string. Implementation has to set this if user was found.
+ * @param priv_algo is a pointer to u8_t. The implementation has to set this if user was found.
+ * @param priv_key is a pointer to a pointer to a string. Implementation has to set this if user was found.
+ */
+err_t
+snmpv3_get_user(const char* username, u8_t *auth_algo, u8_t *auth_key, u8_t *priv_algo, u8_t *priv_key)
+{
+  const char* engine_id;
+  u8_t engine_id_len;
+  
+  if(strlen(username) == 0) {
+    return ERR_OK;
+  }
+  
+  if(memcmp(username, "lwip", 4) != 0) {
+    return ERR_VAL;
+  }
+  
+  snmpv3_get_engine_id(&engine_id, &engine_id_len);
+  
+  if(auth_key != NULL) {
+    snmpv3_password_to_key_sha((const u8_t*)"maplesyrup", 10,
+      (const u8_t*)engine_id, engine_id_len,
+      auth_key);
+    *auth_algo = SNMP_V3_AUTH_ALGO_SHA;
+  }
+  if(priv_key != NULL) {
+    snmpv3_password_to_key_sha((const u8_t*)"maplesyrup", 10,
+      (const u8_t*)engine_id, engine_id_len,
+      priv_key);
+    *priv_algo = SNMP_V3_PRIV_ALGO_DES;
+  }
+  return ERR_OK;
+}
+
+/**
+ * Get engine ID from persistence
+ * @param id
+ * @param len
+ */
+void
+snmpv3_get_engine_id(const char **id, u8_t *len)
+{
+  *id = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02";
+  *len = 12;
+}
+
+/**
+ * Store engine ID in persistence
+ * @param id
+ * @param len
+ */
+err_t
+snmpv3_set_engine_id(const char *id, u8_t len)
+{
+  LWIP_UNUSED_ARG(id);
+  LWIP_UNUSED_ARG(len);
+  return ERR_OK;
+}
+
+/**
+ * Get engine boots from persistence. Must be increased on each boot.
+ * @return 
+ */
+u32_t
+snmpv3_get_engine_boots(void)
+{
+  return 0;
+}
+
+/**
+ * Store engine boots in persistence
+ * @param boots
+ */
+void 
+snmpv3_set_engine_boots(u32_t boots)
+{
+  LWIP_UNUSED_ARG(boots);
+}
+
+/**
+ * RFC3414 2.2.2.
+ * Once the timer reaches 2147483647 it gets reset to zero and the
+ * engine boot ups get incremented.
+ */
+u32_t
+snmpv3_get_engine_time(void)
+{
+  return 0;
+}
+
+/**
+ * Reset current engine time to 0
+ */
+void
+snmpv3_reset_engine_time(void)
+{
+}
+
+#endif /* LWIP_SNMP && LWIP_SNMP_V3 */

+ 331 - 342
libs/thirdparty/lwip_2.1.2/src/apps/snmp/snmpv3_mbedtls.c → libs/thirdparty/LwIP/src/apps/snmp/snmpv3_mbedtls.c

@@ -1,342 +1,331 @@
-/**
- * @file
- * SNMPv3 crypto/auth functions implemented for ARM mbedtls.
- */
-
-/*
- * Copyright (c) 2016 Elias Oenal and Dirk Ziegelmeier.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * Author: Elias Oenal <lwip@eliasoenal.com>
- *         Dirk Ziegelmeier <dirk@ziegelmeier.net>
- */
-
-#include "lwip/apps/snmpv3.h"
-#include "snmpv3_priv.h"
-#include "lwip/arch.h"
-#include "snmp_msg.h"
-#include "lwip/sys.h"
-#include <string.h>
-
-#if LWIP_SNMP && LWIP_SNMP_V3 && LWIP_SNMP_V3_MBEDTLS
-
-#include "mbedtls/md.h"
-#include "mbedtls/cipher.h"
-
-#include "mbedtls/md5.h"
-#include "mbedtls/sha1.h"
-
-err_t
-snmpv3_auth(struct snmp_pbuf_stream *stream, u16_t length,
-            const u8_t *key, snmpv3_auth_algo_t algo, u8_t *hmac_out)
-{
-  u32_t i;
-  u8_t key_len;
-  const mbedtls_md_info_t *md_info;
-  mbedtls_md_context_t ctx;
-  struct snmp_pbuf_stream read_stream;
-  snmp_pbuf_stream_init(&read_stream, stream->pbuf, stream->offset, stream->length);
-
-  if (algo == SNMP_V3_AUTH_ALGO_MD5) {
-    md_info = mbedtls_md_info_from_type(MBEDTLS_MD_MD5);
-    key_len = SNMP_V3_MD5_LEN;
-  } else if (algo == SNMP_V3_AUTH_ALGO_SHA) {
-    md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
-    key_len = SNMP_V3_SHA_LEN;
-  } else {
-    return ERR_ARG;
-  }
-
-  mbedtls_md_init(&ctx);
-  if (mbedtls_md_setup(&ctx, md_info, 1) != 0) {
-    return ERR_ARG;
-  }
-
-  if (mbedtls_md_hmac_starts(&ctx, key, key_len) != 0) {
-    goto free_md;
-  }
-
-  for (i = 0; i < length; i++) {
-    u8_t byte;
-
-    if (snmp_pbuf_stream_read(&read_stream, &byte)) {
-      goto free_md;
-    }
-
-    if (mbedtls_md_hmac_update(&ctx, &byte, 1) != 0) {
-      goto free_md;
-    }
-  }
-
-  if (mbedtls_md_hmac_finish(&ctx, hmac_out) != 0) {
-    goto free_md;
-  }
-
-  mbedtls_md_free(&ctx);
-  return ERR_OK;
-
-free_md:
-  mbedtls_md_free(&ctx);
-  return ERR_ARG;
-}
-
-#if LWIP_SNMP_V3_CRYPTO
-
-err_t
-snmpv3_crypt(struct snmp_pbuf_stream *stream, u16_t length,
-             const u8_t *key, const u8_t *priv_param, const u32_t engine_boots,
-             const u32_t engine_time, snmpv3_priv_algo_t algo, snmpv3_priv_mode_t mode)
-{
-  size_t i;
-  mbedtls_cipher_context_t ctx;
-  const mbedtls_cipher_info_t *cipher_info;
-
-  struct snmp_pbuf_stream read_stream;
-  struct snmp_pbuf_stream write_stream;
-  snmp_pbuf_stream_init(&read_stream, stream->pbuf, stream->offset, stream->length);
-  snmp_pbuf_stream_init(&write_stream, stream->pbuf, stream->offset, stream->length);
-  mbedtls_cipher_init(&ctx);
-
-  if (algo == SNMP_V3_PRIV_ALGO_DES) {
-    u8_t iv_local[8];
-    u8_t out_bytes[8];
-    size_t out_len;
-
-    /* RFC 3414 mandates padding for DES */
-    if ((length & 0x07) != 0) {
-      return ERR_ARG;
-    }
-
-    cipher_info = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_DES_CBC);
-    if (mbedtls_cipher_setup(&ctx, cipher_info) != 0) {
-      return ERR_ARG;
-    }
-    if (mbedtls_cipher_set_padding_mode(&ctx, MBEDTLS_PADDING_NONE) != 0) {
-      return ERR_ARG;
-    }
-    if (mbedtls_cipher_setkey(&ctx, key, 8 * 8, (mode == SNMP_V3_PRIV_MODE_ENCRYPT) ? MBEDTLS_ENCRYPT : MBEDTLS_DECRYPT) != 0) {
-      goto error;
-    }
-
-    /* Prepare IV */
-    for (i = 0; i < LWIP_ARRAYSIZE(iv_local); i++) {
-      iv_local[i] = priv_param[i] ^ key[i + 8];
-    }
-    if (mbedtls_cipher_set_iv(&ctx, iv_local, LWIP_ARRAYSIZE(iv_local)) != 0) {
-      goto error;
-    }
-
-    for (i = 0; i < length; i += 8) {
-      size_t j;
-      u8_t in_bytes[8];
-      out_len = LWIP_ARRAYSIZE(out_bytes) ;
-
-      for (j = 0; j < LWIP_ARRAYSIZE(in_bytes); j++) {
-        if (snmp_pbuf_stream_read(&read_stream, &in_bytes[j]) != ERR_OK) {
-          goto error;
-        }
-      }
-
-      if (mbedtls_cipher_update(&ctx, in_bytes, LWIP_ARRAYSIZE(in_bytes), out_bytes, &out_len) != 0) {
-        goto error;
-      }
-
-      if (snmp_pbuf_stream_writebuf(&write_stream, out_bytes, (u16_t)out_len) != ERR_OK) {
-        goto error;
-      }
-    }
-
-    out_len = LWIP_ARRAYSIZE(out_bytes);
-    if (mbedtls_cipher_finish(&ctx, out_bytes, &out_len) != 0) {
-      goto error;
-    }
-
-    if (snmp_pbuf_stream_writebuf(&write_stream, out_bytes, (u16_t)out_len) != ERR_OK) {
-      goto error;
-    }
-  } else if (algo == SNMP_V3_PRIV_ALGO_AES) {
-    u8_t iv_local[16];
-
-    cipher_info = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_AES_128_CFB128);
-    if (mbedtls_cipher_setup(&ctx, cipher_info) != 0) {
-      return ERR_ARG;
-    }
-    if (mbedtls_cipher_setkey(&ctx, key, 16 * 8, (mode == SNMP_V3_PRIV_MODE_ENCRYPT) ? MBEDTLS_ENCRYPT : MBEDTLS_DECRYPT) != 0) {
-      goto error;
-    }
-
-    /*
-     * IV is the big endian concatenation of boots,
-     * uptime and priv param - see RFC3826.
-     */
-    iv_local[0 + 0] = (engine_boots >> 24) & 0xFF;
-    iv_local[0 + 1] = (engine_boots >> 16) & 0xFF;
-    iv_local[0 + 2] = (engine_boots >>  8) & 0xFF;
-    iv_local[0 + 3] = (engine_boots >>  0) & 0xFF;
-    iv_local[4 + 0] = (engine_time  >> 24) & 0xFF;
-    iv_local[4 + 1] = (engine_time  >> 16) & 0xFF;
-    iv_local[4 + 2] = (engine_time  >>  8) & 0xFF;
-    iv_local[4 + 3] = (engine_time  >>  0) & 0xFF;
-    SMEMCPY(iv_local + 8, priv_param, 8);
-    if (mbedtls_cipher_set_iv(&ctx, iv_local, LWIP_ARRAYSIZE(iv_local)) != 0) {
-      goto error;
-    }
-
-    for (i = 0; i < length; i++) {
-      u8_t in_byte;
-      u8_t out_byte;
-      size_t out_len = sizeof(out_byte);
-
-      if (snmp_pbuf_stream_read(&read_stream, &in_byte) != ERR_OK) {
-        goto error;
-      }
-      if (mbedtls_cipher_update(&ctx, &in_byte, sizeof(in_byte), &out_byte, &out_len) != 0) {
-        goto error;
-      }
-      if (snmp_pbuf_stream_write(&write_stream, out_byte) != ERR_OK) {
-        goto error;
-      }
-    }
-  } else {
-    return ERR_ARG;
-  }
-
-  mbedtls_cipher_free(&ctx);
-  return ERR_OK;
-
-error:
-  mbedtls_cipher_free(&ctx);
-  return ERR_OK;
-}
-
-#endif /* LWIP_SNMP_V3_CRYPTO */
-
-/* A.2.1. Password to Key Sample Code for MD5 */
-void
-snmpv3_password_to_key_md5(
-  const u8_t *password,    /* IN */
-  size_t      passwordlen, /* IN */
-  const u8_t *engineID,    /* IN  - pointer to snmpEngineID  */
-  u8_t        engineLength,/* IN  - length of snmpEngineID */
-  u8_t       *key)         /* OUT - pointer to caller 16-octet buffer */
-{
-  mbedtls_md5_context MD;
-  u8_t *cp, password_buf[64];
-  u32_t password_index = 0;
-  u8_t i;
-  u32_t count = 0;
-
-  mbedtls_md5_init(&MD); /* initialize MD5 */
-  mbedtls_md5_starts(&MD);
-
-  /**********************************************/
-  /* Use while loop until we've done 1 Megabyte */
-  /**********************************************/
-  while (count < 1048576) {
-    cp = password_buf;
-    for (i = 0; i < 64; i++) {
-      /*************************************************/
-      /* Take the next octet of the password, wrapping */
-      /* to the beginning of the password as necessary.*/
-      /*************************************************/
-      *cp++ = password[password_index++ % passwordlen];
-    }
-    mbedtls_md5_update(&MD, password_buf, 64);
-    count += 64;
-  }
-  mbedtls_md5_finish(&MD, key); /* tell MD5 we're done */
-
-  /*****************************************************/
-  /* Now localize the key with the engineID and pass   */
-  /* through MD5 to produce final key                  */
-  /* May want to ensure that engineLength <= 32,       */
-  /* otherwise need to use a buffer larger than 64     */
-  /*****************************************************/
-  SMEMCPY(password_buf, key, 16);
-  MEMCPY(password_buf + 16, engineID, engineLength);
-  SMEMCPY(password_buf + 16 + engineLength, key, 16);
-
-  mbedtls_md5_starts(&MD);
-  mbedtls_md5_update(&MD, password_buf, 32 + engineLength);
-  mbedtls_md5_finish(&MD, key);
-
-  mbedtls_md5_free(&MD);
-  return;
-}
-
-/* A.2.2. Password to Key Sample Code for SHA */
-void
-snmpv3_password_to_key_sha(
-  const u8_t *password,    /* IN */
-  size_t      passwordlen, /* IN */
-  const u8_t *engineID,    /* IN  - pointer to snmpEngineID  */
-  u8_t        engineLength,/* IN  - length of snmpEngineID */
-  u8_t       *key)         /* OUT - pointer to caller 20-octet buffer */
-{
-  mbedtls_sha1_context SH;
-  u8_t *cp, password_buf[72];
-  u32_t password_index = 0;
-  u8_t i;
-  u32_t count = 0;
-
-  mbedtls_sha1_init(&SH); /* initialize SHA */
-  mbedtls_sha1_starts(&SH);
-
-  /**********************************************/
-  /* Use while loop until we've done 1 Megabyte */
-  /**********************************************/
-  while (count < 1048576) {
-    cp = password_buf;
-    for (i = 0; i < 64; i++) {
-      /*************************************************/
-      /* Take the next octet of the password, wrapping */
-      /* to the beginning of the password as necessary.*/
-      /*************************************************/
-      *cp++ = password[password_index++ % passwordlen];
-    }
-    mbedtls_sha1_update(&SH, password_buf, 64);
-    count += 64;
-  }
-  mbedtls_sha1_finish(&SH, key); /* tell SHA we're done */
-
-  /*****************************************************/
-  /* Now localize the key with the engineID and pass   */
-  /* through SHA to produce final key                  */
-  /* May want to ensure that engineLength <= 32,       */
-  /* otherwise need to use a buffer larger than 72     */
-  /*****************************************************/
-  SMEMCPY(password_buf, key, 20);
-  MEMCPY(password_buf + 20, engineID, engineLength);
-  SMEMCPY(password_buf + 20 + engineLength, key, 20);
-
-  mbedtls_sha1_starts(&SH);
-  mbedtls_sha1_update(&SH, password_buf, 40 + engineLength);
-  mbedtls_sha1_finish(&SH, key);
-
-  mbedtls_sha1_free(&SH);
-  return;
-}
-
-#endif /* LWIP_SNMP && LWIP_SNMP_V3 && LWIP_SNMP_V3_MBEDTLS */
+/**
+ * @file
+ * SNMPv3 crypto/auth functions implemented for ARM mbedtls.
+ */
+
+/*
+ * Copyright (c) 2016 Elias Oenal and Dirk Ziegelmeier.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Elias Oenal <lwip@eliasoenal.com>
+ *         Dirk Ziegelmeier <dirk@ziegelmeier.net>
+ */
+
+#include "lwip/apps/snmpv3.h"
+#include "snmpv3_priv.h"
+#include "lwip/arch.h"
+#include "snmp_msg.h"
+#include "lwip/sys.h"
+#include <string.h>
+
+#if LWIP_SNMP && LWIP_SNMP_V3 && LWIP_SNMP_V3_MBEDTLS
+
+#include "mbedtls/md.h"
+#include "mbedtls/cipher.h"
+
+#include "mbedtls/md5.h"
+#include "mbedtls/sha1.h"
+
+err_t
+snmpv3_auth(struct snmp_pbuf_stream* stream, u16_t length,
+    const u8_t* key, u8_t algo, u8_t* hmac_out)
+{
+  u32_t i;
+  u8_t key_len;
+  const mbedtls_md_info_t *md_info;
+  mbedtls_md_context_t ctx;
+  struct snmp_pbuf_stream read_stream;
+  snmp_pbuf_stream_init(&read_stream, stream->pbuf, stream->offset, stream->length);
+
+  if (algo == SNMP_V3_AUTH_ALGO_MD5) {
+    md_info = mbedtls_md_info_from_type(MBEDTLS_MD_MD5);
+    key_len = SNMP_V3_MD5_LEN;
+  } else if (algo == SNMP_V3_AUTH_ALGO_SHA) {
+    md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
+    key_len = SNMP_V3_SHA_LEN;
+  } else {
+    return ERR_ARG;
+  }
+
+  mbedtls_md_init(&ctx);
+  if(mbedtls_md_setup(&ctx, md_info, 1) != 0) {
+    return ERR_ARG;
+  }
+          
+  if (mbedtls_md_hmac_starts(&ctx, key, key_len) != 0) {
+    goto free_md;
+  }
+
+  for (i = 0; i < length; i++) {
+    u8_t byte;
+
+    if (snmp_pbuf_stream_read(&read_stream, &byte)) {
+      goto free_md;
+    }
+
+    if (mbedtls_md_hmac_update(&ctx, &byte, 1) != 0) {
+      goto free_md;
+    }
+  }
+
+  if (mbedtls_md_hmac_finish(&ctx, hmac_out) != 0) {
+    goto free_md;
+  }
+
+  mbedtls_md_free(&ctx);
+  return ERR_OK;
+  
+free_md:
+  mbedtls_md_free(&ctx);
+  return ERR_ARG;
+}
+
+#if LWIP_SNMP_V3_CRYPTO
+
+err_t
+snmpv3_crypt(struct snmp_pbuf_stream* stream, u16_t length,
+    const u8_t* key, const u8_t* priv_param, const u32_t engine_boots,
+    const u32_t engine_time, u8_t algo, u8_t mode)
+{
+  size_t i;
+  mbedtls_cipher_context_t ctx;
+  const mbedtls_cipher_info_t *cipher_info;
+
+  struct snmp_pbuf_stream read_stream;
+  struct snmp_pbuf_stream write_stream;
+  snmp_pbuf_stream_init(&read_stream, stream->pbuf, stream->offset, stream->length);
+  snmp_pbuf_stream_init(&write_stream, stream->pbuf, stream->offset, stream->length);
+  mbedtls_cipher_init(&ctx);
+
+  if (algo == SNMP_V3_PRIV_ALGO_DES) {
+    u8_t iv_local[8];
+    u8_t out_bytes[8];
+    size_t out_len;
+
+    /* RFC 3414 mandates padding for DES */
+    if ((length & 0x07) != 0) {
+      return ERR_ARG;
+    }
+
+    cipher_info = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_DES_CBC);
+    if(mbedtls_cipher_setup(&ctx, cipher_info) != 0) {
+      return ERR_ARG;
+    }
+    if(mbedtls_cipher_set_padding_mode(&ctx, MBEDTLS_PADDING_NONE) != 0) {
+      return ERR_ARG;
+    }
+    if(mbedtls_cipher_setkey(&ctx, key, 8*8, (mode == SNMP_V3_PRIV_MODE_ENCRYPT)? MBEDTLS_ENCRYPT : MBEDTLS_DECRYPT) != 0) {
+      goto error;
+    }
+
+    /* Prepare IV */    
+    for (i = 0; i < LWIP_ARRAYSIZE(iv_local); i++) {
+      iv_local[i] = priv_param[i] ^ key[i + 8];
+    }
+    if(mbedtls_cipher_set_iv(&ctx, iv_local, LWIP_ARRAYSIZE(iv_local)) != 0) {
+      goto error;
+    }
+
+    for (i = 0; i < length; i += 8) {
+      size_t j;
+      u8_t in_bytes[8];
+      out_len = LWIP_ARRAYSIZE(out_bytes) ;
+      
+      for (j = 0; j < LWIP_ARRAYSIZE(in_bytes); j++) {
+        snmp_pbuf_stream_read(&read_stream, &in_bytes[j]);
+      }
+
+      if(mbedtls_cipher_update(&ctx, in_bytes, LWIP_ARRAYSIZE(in_bytes), out_bytes, &out_len) != 0) {
+        goto error;
+      }
+
+      snmp_pbuf_stream_writebuf(&write_stream, out_bytes, out_len);
+    }
+    
+    out_len = LWIP_ARRAYSIZE(out_bytes);
+    if(mbedtls_cipher_finish(&ctx, out_bytes, &out_len) != 0) {
+      goto error;
+    }
+    snmp_pbuf_stream_writebuf(&write_stream, out_bytes, out_len);
+  } else if (algo == SNMP_V3_PRIV_ALGO_AES) {
+    u8_t iv_local[16];
+
+    cipher_info = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_AES_128_CFB128);
+    if(mbedtls_cipher_setup(&ctx, cipher_info) != 0) {
+      return ERR_ARG;
+    }
+    if(mbedtls_cipher_setkey(&ctx, key, 16*8, (mode == SNMP_V3_PRIV_MODE_ENCRYPT)? MBEDTLS_ENCRYPT : MBEDTLS_DECRYPT) != 0) {
+      goto error;
+    }
+
+    /*
+     * IV is the big endian concatenation of boots,
+     * uptime and priv param - see RFC3826.
+     */
+    iv_local[0 + 0] = (engine_boots >> 24) & 0xFF;
+    iv_local[0 + 1] = (engine_boots >> 16) & 0xFF;
+    iv_local[0 + 2] = (engine_boots >>  8) & 0xFF;
+    iv_local[0 + 3] = (engine_boots >>  0) & 0xFF;
+    iv_local[4 + 0] = (engine_time  >> 24) & 0xFF;
+    iv_local[4 + 1] = (engine_time  >> 16) & 0xFF;
+    iv_local[4 + 2] = (engine_time  >>  8) & 0xFF;
+    iv_local[4 + 3] = (engine_time  >>  0) & 0xFF;
+    SMEMCPY(iv_local + 8, priv_param, 8);
+    if(mbedtls_cipher_set_iv(&ctx, iv_local, LWIP_ARRAYSIZE(iv_local)) != 0) {
+      goto error;
+    }
+
+    for (i = 0; i < length; i++) {
+      u8_t in_byte;
+      u8_t out_byte;
+      size_t out_len = sizeof(out_byte);
+      
+      snmp_pbuf_stream_read(&read_stream, &in_byte);
+      if(mbedtls_cipher_update(&ctx, &in_byte, sizeof(in_byte), &out_byte, &out_len) != 0) {
+        goto error;
+      }
+      snmp_pbuf_stream_write(&write_stream, out_byte);
+    }
+  } else {
+    return ERR_ARG;
+  }
+
+  mbedtls_cipher_free(&ctx);
+  return ERR_OK;
+
+error:
+  mbedtls_cipher_free(&ctx);
+  return ERR_OK;
+}
+
+#endif /* LWIP_SNMP_V3_CRYPTO */
+
+/* A.2.1. Password to Key Sample Code for MD5 */
+void 
+snmpv3_password_to_key_md5(
+    const u8_t *password,    /* IN */
+    u8_t        passwordlen, /* IN */
+    const u8_t *engineID,    /* IN  - pointer to snmpEngineID  */
+    u8_t        engineLength,/* IN  - length of snmpEngineID */
+    u8_t       *key)         /* OUT - pointer to caller 16-octet buffer */
+{
+  mbedtls_md5_context MD;
+  u8_t *cp, password_buf[64];
+  u32_t password_index = 0;
+  u8_t i;
+  u32_t count = 0;
+
+  mbedtls_md5_init(&MD); /* initialize MD5 */
+  mbedtls_md5_starts(&MD);
+
+  /**********************************************/
+  /* Use while loop until we've done 1 Megabyte */
+  /**********************************************/
+  while (count < 1048576) {
+    cp = password_buf;
+    for (i = 0; i < 64; i++) {
+      /*************************************************/
+      /* Take the next octet of the password, wrapping */
+      /* to the beginning of the password as necessary.*/
+      /*************************************************/
+      *cp++ = password[password_index++ % passwordlen];
+    }
+    mbedtls_md5_update(&MD, password_buf, 64);
+    count += 64;
+  }
+  mbedtls_md5_finish(&MD, key); /* tell MD5 we're done */
+
+  /*****************************************************/
+  /* Now localize the key with the engineID and pass   */
+  /* through MD5 to produce final key                  */
+  /* May want to ensure that engineLength <= 32,       */
+  /* otherwise need to use a buffer larger than 64     */
+  /*****************************************************/
+  SMEMCPY(password_buf, key, 16);
+  MEMCPY(password_buf + 16, engineID, engineLength);
+  SMEMCPY(password_buf + 16 + engineLength, key, 16);
+
+  mbedtls_md5_starts(&MD);
+  mbedtls_md5_update(&MD, password_buf, 32 + engineLength);
+  mbedtls_md5_finish(&MD, key);
+
+  mbedtls_md5_free(&MD);
+  return;
+}
+
+/* A.2.2. Password to Key Sample Code for SHA */
+void 
+snmpv3_password_to_key_sha(
+    const u8_t *password,    /* IN */
+    u8_t        passwordlen, /* IN */
+    const u8_t *engineID,    /* IN  - pointer to snmpEngineID  */
+    u8_t        engineLength,/* IN  - length of snmpEngineID */
+    u8_t       *key)         /* OUT - pointer to caller 20-octet buffer */
+{
+  mbedtls_sha1_context SH;
+  u8_t *cp, password_buf[72];
+  u32_t password_index = 0;
+  u8_t i;
+  u32_t count = 0;
+
+  mbedtls_sha1_init(&SH); /* initialize SHA */
+  mbedtls_sha1_starts(&SH);
+
+  /**********************************************/
+  /* Use while loop until we've done 1 Megabyte */
+  /**********************************************/
+  while (count < 1048576) {
+    cp = password_buf;
+    for (i = 0; i < 64; i++) {
+      /*************************************************/
+      /* Take the next octet of the password, wrapping */
+      /* to the beginning of the password as necessary.*/
+      /*************************************************/
+      *cp++ = password[password_index++ % passwordlen];
+    }
+    mbedtls_sha1_update(&SH, password_buf, 64);
+    count += 64;
+  }
+  mbedtls_sha1_finish(&SH, key); /* tell SHA we're done */
+
+  /*****************************************************/
+  /* Now localize the key with the engineID and pass   */
+  /* through SHA to produce final key                  */
+  /* May want to ensure that engineLength <= 32,       */
+  /* otherwise need to use a buffer larger than 72     */
+  /*****************************************************/
+  SMEMCPY(password_buf, key, 20);
+  MEMCPY(password_buf + 20, engineID, engineLength);
+  SMEMCPY(password_buf + 20 + engineLength, key, 20);
+
+  mbedtls_sha1_starts(&SH);
+  mbedtls_sha1_update(&SH, password_buf, 40 + engineLength);
+  mbedtls_sha1_finish(&SH, key);
+  
+  mbedtls_sha1_free(&SH);
+  return;
+}
+
+#endif /* LWIP_SNMP && LWIP_SNMP_V3 && LWIP_SNMP_V3_MBEDTLS */

+ 66 - 69
libs/thirdparty/lwip_2.1.2/src/apps/snmp/snmpv3_priv.h → libs/thirdparty/LwIP/src/apps/snmp/snmpv3_priv.h

@@ -1,69 +1,66 @@
-/**
- * @file
- * Additional SNMPv3 functionality RFC3414 and RFC3826 (internal API, do not use in client code).
- */
-
-/*
- * Copyright (c) 2016 Elias Oenal.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * Author: Elias Oenal <lwip@eliasoenal.com>
- */
-
-#ifndef LWIP_HDR_APPS_SNMP_V3_PRIV_H
-#define LWIP_HDR_APPS_SNMP_V3_PRIV_H
-
-#include "lwip/apps/snmp_opts.h"
-
-#if LWIP_SNMP && LWIP_SNMP_V3
-
-#include "lwip/apps/snmpv3.h"
-#include "snmp_pbuf_stream.h"
-
-/* According to RFC 3411 */
-#define SNMP_V3_MAX_ENGINE_ID_LENGTH  32
-#define SNMP_V3_MAX_USER_LENGTH       32
-
-#define SNMP_V3_MAX_AUTH_PARAM_LENGTH  12
-#define SNMP_V3_MAX_PRIV_PARAM_LENGTH  8
-
-#define SNMP_V3_MD5_LEN        16
-#define SNMP_V3_SHA_LEN        20
-
-typedef enum {
-  SNMP_V3_PRIV_MODE_DECRYPT = 0,
-  SNMP_V3_PRIV_MODE_ENCRYPT = 1
-} snmpv3_priv_mode_t;
-
-s32_t snmpv3_get_engine_boots_internal(void);
-err_t snmpv3_auth(struct snmp_pbuf_stream *stream, u16_t length, const u8_t *key, snmpv3_auth_algo_t algo, u8_t *hmac_out);
-err_t snmpv3_crypt(struct snmp_pbuf_stream *stream, u16_t length, const u8_t *key,
-                   const u8_t *priv_param, const u32_t engine_boots, const u32_t engine_time, snmpv3_priv_algo_t algo, snmpv3_priv_mode_t mode);
-err_t snmpv3_build_priv_param(u8_t *priv_param);
-void snmpv3_enginetime_timer(void *arg);
-
-#endif
-
-#endif /* LWIP_HDR_APPS_SNMP_V3_PRIV_H */
+/**
+ * @file
+ * Additional SNMPv3 functionality RFC3414 and RFC3826 (internal API, do not use in client code).
+ */
+
+/*
+ * Copyright (c) 2016 Elias Oenal.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Elias Oenal <lwip@eliasoenal.com>
+ */
+
+#ifndef LWIP_HDR_APPS_SNMP_V3_PRIV_H
+#define LWIP_HDR_APPS_SNMP_V3_PRIV_H
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP && LWIP_SNMP_V3
+
+#include "snmp_pbuf_stream.h"
+
+/* According to RFC 3411 */
+#define SNMP_V3_MAX_ENGINE_ID_LENGTH  32
+#define SNMP_V3_MAX_USER_LENGTH       32
+
+#define SNMP_V3_MAX_AUTH_PARAM_LENGTH  12
+#define SNMP_V3_MAX_PRIV_PARAM_LENGTH  8
+
+#define SNMP_V3_AUTH_FLAG      0x01
+#define SNMP_V3_PRIV_FLAG      0x02
+
+#define SNMP_V3_MD5_LEN        16
+#define SNMP_V3_SHA_LEN        20
+
+u32_t snmpv3_get_engine_boots_internal(void);
+u32_t snmpv3_get_engine_time_internal(void);
+err_t snmpv3_auth(struct snmp_pbuf_stream* stream, u16_t length, const u8_t* key, u8_t algo, u8_t* hmac_out);
+err_t snmpv3_crypt(struct snmp_pbuf_stream* stream, u16_t length, const u8_t* key,
+    const u8_t* priv_param, const u32_t engine_boots, const u32_t engine_time, u8_t algo, u8_t mode);
+err_t snmpv3_build_priv_param(u8_t* priv_param);
+
+#endif
+
+#endif /* LWIP_HDR_APPS_SNMP_V3_PRIV_H */

+ 727 - 869
libs/thirdparty/lwip_2.1.2/src/apps/sntp/sntp.c → libs/thirdparty/LwIP/src/apps/sntp/sntp.c

@@ -1,869 +1,727 @@
-/**
- * @file
- * SNTP client module
- */
-
-/*
- * Copyright (c) 2007-2009 Frédéric Bernon, Simon Goldschmidt
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- *
- * Author: Frédéric Bernon, Simon Goldschmidt
- */
-
-
-/**
- * @defgroup sntp SNTP
- * @ingroup apps
- *
- * This is simple "SNTP" client for the lwIP raw API.
- * It is a minimal implementation of SNTPv4 as specified in RFC 4330.
- *
- * For a list of some public NTP servers, see this link:
- * http://support.ntp.org/bin/view/Servers/NTPPoolServers
- *
- * @todo:
- * - complete SNTP_CHECK_RESPONSE checks 3 and 4
- */
-
-#include "lwip/apps/sntp.h"
-
-#include "lwip/opt.h"
-#include "lwip/timeouts.h"
-#include "lwip/udp.h"
-#include "lwip/dns.h"
-#include "lwip/ip_addr.h"
-#include "lwip/pbuf.h"
-#include "lwip/dhcp.h"
-
-#include <string.h>
-#include <time.h>
-
-#if LWIP_UDP
-
-/* Handle support for more than one server via SNTP_MAX_SERVERS */
-#if SNTP_MAX_SERVERS > 1
-#define SNTP_SUPPORT_MULTIPLE_SERVERS 1
-#else /* NTP_MAX_SERVERS > 1 */
-#define SNTP_SUPPORT_MULTIPLE_SERVERS 0
-#endif /* NTP_MAX_SERVERS > 1 */
-
-#ifndef SNTP_SUPPRESS_DELAY_CHECK
-#if SNTP_UPDATE_DELAY < 15000
-#error "SNTPv4 RFC 4330 enforces a minimum update time of 15 seconds (define SNTP_SUPPRESS_DELAY_CHECK to disable this error)!"
-#endif
-#endif
-
-/* the various debug levels for this file */
-#define SNTP_DEBUG_TRACE        (SNTP_DEBUG | LWIP_DBG_TRACE)
-#define SNTP_DEBUG_STATE        (SNTP_DEBUG | LWIP_DBG_STATE)
-#define SNTP_DEBUG_WARN         (SNTP_DEBUG | LWIP_DBG_LEVEL_WARNING)
-#define SNTP_DEBUG_WARN_STATE   (SNTP_DEBUG | LWIP_DBG_LEVEL_WARNING | LWIP_DBG_STATE)
-#define SNTP_DEBUG_SERIOUS      (SNTP_DEBUG | LWIP_DBG_LEVEL_SERIOUS)
-
-#define SNTP_ERR_KOD                1
-
-/* SNTP protocol defines */
-#define SNTP_MSG_LEN                48
-
-#define SNTP_OFFSET_LI_VN_MODE      0
-#define SNTP_LI_MASK                0xC0
-#define SNTP_LI_NO_WARNING          (0x00 << 6)
-#define SNTP_LI_LAST_MINUTE_61_SEC  (0x01 << 6)
-#define SNTP_LI_LAST_MINUTE_59_SEC  (0x02 << 6)
-#define SNTP_LI_ALARM_CONDITION     (0x03 << 6) /* (clock not synchronized) */
-
-#define SNTP_VERSION_MASK           0x38
-#define SNTP_VERSION                (4/* NTP Version 4*/<<3)
-
-#define SNTP_MODE_MASK              0x07
-#define SNTP_MODE_CLIENT            0x03
-#define SNTP_MODE_SERVER            0x04
-#define SNTP_MODE_BROADCAST         0x05
-
-#define SNTP_OFFSET_STRATUM         1
-#define SNTP_STRATUM_KOD            0x00
-
-#define SNTP_OFFSET_ORIGINATE_TIME  24
-#define SNTP_OFFSET_RECEIVE_TIME    32
-#define SNTP_OFFSET_TRANSMIT_TIME   40
-
-/* Number of seconds between 1970 and Feb 7, 2036 06:28:16 UTC (epoch 1) */
-#define DIFF_SEC_1970_2036          ((u32_t)2085978496L)
-
-/** Convert NTP timestamp fraction to microseconds.
- */
-#ifndef SNTP_FRAC_TO_US
-# if LWIP_HAVE_INT64
-#  define SNTP_FRAC_TO_US(f)        ((u32_t)(((u64_t)(f) * 1000000UL) >> 32))
-# else
-#  define SNTP_FRAC_TO_US(f)        ((u32_t)(f) / 4295)
-# endif
-#endif /* !SNTP_FRAC_TO_US */
-
-/* Configure behaviour depending on native, microsecond or second precision.
- * Treat NTP timestamps as signed two's-complement integers. This way,
- * timestamps that have the MSB set simply become negative offsets from
- * the epoch (Feb 7, 2036 06:28:16 UTC). Representable dates range from
- * 1968 to 2104.
- */
-#ifndef SNTP_SET_SYSTEM_TIME_NTP
-# ifdef SNTP_SET_SYSTEM_TIME_US
-#  define SNTP_SET_SYSTEM_TIME_NTP(s, f) \
-    SNTP_SET_SYSTEM_TIME_US((u32_t)((s) + DIFF_SEC_1970_2036), SNTP_FRAC_TO_US(f))
-# else
-#  define SNTP_SET_SYSTEM_TIME_NTP(s, f) \
-    SNTP_SET_SYSTEM_TIME((u32_t)((s) + DIFF_SEC_1970_2036))
-# endif
-#endif /* !SNTP_SET_SYSTEM_TIME_NTP */
-
-/* Get the system time either natively as NTP timestamp or convert from
- * Unix time in seconds and microseconds. Take care to avoid overflow if the
- * microsecond value is at the maximum of 999999. Also add 0.5 us fudge to
- * avoid special values like 0, and to mask round-off errors that would
- * otherwise break round-trip conversion identity.
- */
-#ifndef SNTP_GET_SYSTEM_TIME_NTP
-# define SNTP_GET_SYSTEM_TIME_NTP(s, f) do { \
-    u32_t sec_, usec_; \
-    SNTP_GET_SYSTEM_TIME(sec_, usec_); \
-    (s) = (s32_t)(sec_ - DIFF_SEC_1970_2036); \
-    (f) = usec_ * 4295 - ((usec_ * 2143) >> 16) + 2147; \
-  } while (0)
-#endif /* !SNTP_GET_SYSTEM_TIME_NTP */
-
-/* Start offset of the timestamps to extract from the SNTP packet */
-#define SNTP_OFFSET_TIMESTAMPS \
-    (SNTP_OFFSET_TRANSMIT_TIME + 8 - sizeof(struct sntp_timestamps))
-
-/* Round-trip delay arithmetic helpers */
-#if SNTP_COMP_ROUNDTRIP
-# if !LWIP_HAVE_INT64
-#  error "SNTP round-trip delay compensation requires 64-bit arithmetic"
-# endif
-# define SNTP_SEC_FRAC_TO_S64(s, f) \
-    ((s64_t)(((u64_t)(s) << 32) | (u32_t)(f)))
-# define SNTP_TIMESTAMP_TO_S64(t) \
-    SNTP_SEC_FRAC_TO_S64(lwip_ntohl((t).sec), lwip_ntohl((t).frac))
-#endif /* SNTP_COMP_ROUNDTRIP */
-
-/**
- * 64-bit NTP timestamp, in network byte order.
- */
-struct sntp_time {
-  u32_t sec;
-  u32_t frac;
-};
-
-/**
- * Timestamps to be extracted from the NTP header.
- */
-struct sntp_timestamps {
-#if SNTP_COMP_ROUNDTRIP || SNTP_CHECK_RESPONSE >= 2
-  struct sntp_time orig;
-  struct sntp_time recv;
-#endif
-  struct sntp_time xmit;
-};
-
-/**
- * SNTP packet format (without optional fields)
- * Timestamps are coded as 64 bits:
- * - signed 32 bits seconds since Feb 07, 2036, 06:28:16 UTC (epoch 1)
- * - unsigned 32 bits seconds fraction (2^32 = 1 second)
- */
-#ifdef PACK_STRUCT_USE_INCLUDES
-#  include "arch/bpstruct.h"
-#endif
-PACK_STRUCT_BEGIN
-struct sntp_msg {
-  PACK_STRUCT_FLD_8(u8_t  li_vn_mode);
-  PACK_STRUCT_FLD_8(u8_t  stratum);
-  PACK_STRUCT_FLD_8(u8_t  poll);
-  PACK_STRUCT_FLD_8(u8_t  precision);
-  PACK_STRUCT_FIELD(u32_t root_delay);
-  PACK_STRUCT_FIELD(u32_t root_dispersion);
-  PACK_STRUCT_FIELD(u32_t reference_identifier);
-  PACK_STRUCT_FIELD(u32_t reference_timestamp[2]);
-  PACK_STRUCT_FIELD(u32_t originate_timestamp[2]);
-  PACK_STRUCT_FIELD(u32_t receive_timestamp[2]);
-  PACK_STRUCT_FIELD(u32_t transmit_timestamp[2]);
-} PACK_STRUCT_STRUCT;
-PACK_STRUCT_END
-#ifdef PACK_STRUCT_USE_INCLUDES
-#  include "arch/epstruct.h"
-#endif
-
-/* function prototypes */
-static void sntp_request(void *arg);
-
-/** The operating mode */
-static u8_t sntp_opmode;
-
-/** The UDP pcb used by the SNTP client */
-static struct udp_pcb *sntp_pcb;
-/** Names/Addresses of servers */
-struct sntp_server {
-#if SNTP_SERVER_DNS
-  const char *name;
-#endif /* SNTP_SERVER_DNS */
-  ip_addr_t addr;
-#if SNTP_MONITOR_SERVER_REACHABILITY
-  /** Reachability shift register as described in RFC 5905 */
-  u8_t reachability;
-#endif /* SNTP_MONITOR_SERVER_REACHABILITY */
-};
-static struct sntp_server sntp_servers[SNTP_MAX_SERVERS];
-
-#if SNTP_GET_SERVERS_FROM_DHCP
-static u8_t sntp_set_servers_from_dhcp;
-#endif /* SNTP_GET_SERVERS_FROM_DHCP */
-#if SNTP_SUPPORT_MULTIPLE_SERVERS
-/** The currently used server (initialized to 0) */
-static u8_t sntp_current_server;
-#else /* SNTP_SUPPORT_MULTIPLE_SERVERS */
-#define sntp_current_server 0
-#endif /* SNTP_SUPPORT_MULTIPLE_SERVERS */
-
-#if SNTP_RETRY_TIMEOUT_EXP
-#define SNTP_RESET_RETRY_TIMEOUT() sntp_retry_timeout = SNTP_RETRY_TIMEOUT
-/** Retry time, initialized with SNTP_RETRY_TIMEOUT and doubled with each retry. */
-static u32_t sntp_retry_timeout;
-#else /* SNTP_RETRY_TIMEOUT_EXP */
-#define SNTP_RESET_RETRY_TIMEOUT()
-#define sntp_retry_timeout SNTP_RETRY_TIMEOUT
-#endif /* SNTP_RETRY_TIMEOUT_EXP */
-
-#if SNTP_CHECK_RESPONSE >= 1
-/** Saves the last server address to compare with response */
-static ip_addr_t sntp_last_server_address;
-#endif /* SNTP_CHECK_RESPONSE >= 1 */
-
-#if SNTP_CHECK_RESPONSE >= 2
-/** Saves the last timestamp sent (which is sent back by the server)
- * to compare against in response. Stored in network byte order. */
-static struct sntp_time sntp_last_timestamp_sent;
-#endif /* SNTP_CHECK_RESPONSE >= 2 */
-
-#if defined(LWIP_DEBUG) && !defined(sntp_format_time)
-/* Debug print helper. */
-static const char *
-sntp_format_time(s32_t sec)
-{
-  time_t ut;
-  ut = (u32_t)((u32_t)sec + DIFF_SEC_1970_2036);
-  return ctime(&ut);
-}
-#endif /* LWIP_DEBUG && !sntp_format_time */
-
-/**
- * SNTP processing of received timestamp
- */
-static void
-sntp_process(const struct sntp_timestamps *timestamps)
-{
-  s32_t sec;
-  u32_t frac;
-
-  sec  = (s32_t)lwip_ntohl(timestamps->xmit.sec);
-  frac = lwip_ntohl(timestamps->xmit.frac);
-
-#if SNTP_COMP_ROUNDTRIP
-# if SNTP_CHECK_RESPONSE >= 2
-  if (timestamps->recv.sec != 0 || timestamps->recv.frac != 0)
-# endif
-  {
-    s32_t dest_sec;
-    u32_t dest_frac;
-    u32_t step_sec;
-
-    /* Get the destination time stamp, i.e. the current system time */
-    SNTP_GET_SYSTEM_TIME_NTP(dest_sec, dest_frac);
-
-    step_sec = (dest_sec < sec) ? ((u32_t)sec - (u32_t)dest_sec)
-               : ((u32_t)dest_sec - (u32_t)sec);
-    /* In order to avoid overflows, skip the compensation if the clock step
-     * is larger than about 34 years. */
-    if ((step_sec >> 30) == 0) {
-      s64_t t1, t2, t3, t4;
-
-      t4 = SNTP_SEC_FRAC_TO_S64(dest_sec, dest_frac);
-      t3 = SNTP_SEC_FRAC_TO_S64(sec, frac);
-      t1 = SNTP_TIMESTAMP_TO_S64(timestamps->orig);
-      t2 = SNTP_TIMESTAMP_TO_S64(timestamps->recv);
-      /* Clock offset calculation according to RFC 4330 */
-      t4 += ((t2 - t1) + (t3 - t4)) / 2;
-
-      sec  = (s32_t)((u64_t)t4 >> 32);
-      frac = (u32_t)((u64_t)t4);
-    }
-  }
-#endif /* SNTP_COMP_ROUNDTRIP */
-
-  SNTP_SET_SYSTEM_TIME_NTP(sec, frac);
-  LWIP_UNUSED_ARG(frac); /* might be unused if only seconds are set */
-  LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp_process: %s, %" U32_F " us\n",
-                                 sntp_format_time(sec), SNTP_FRAC_TO_US(frac)));
-}
-
-/**
- * Initialize request struct to be sent to server.
- */
-static void
-sntp_initialize_request(struct sntp_msg *req)
-{
-  memset(req, 0, SNTP_MSG_LEN);
-  req->li_vn_mode = SNTP_LI_NO_WARNING | SNTP_VERSION | SNTP_MODE_CLIENT;
-
-#if SNTP_CHECK_RESPONSE >= 2 || SNTP_COMP_ROUNDTRIP
-  {
-    s32_t secs;
-    u32_t sec, frac;
-    /* Get the transmit timestamp */
-    SNTP_GET_SYSTEM_TIME_NTP(secs, frac);
-    sec  = lwip_htonl((u32_t)secs);
-    frac = lwip_htonl(frac);
-
-# if SNTP_CHECK_RESPONSE >= 2
-    sntp_last_timestamp_sent.sec  = sec;
-    sntp_last_timestamp_sent.frac = frac;
-# endif
-    req->transmit_timestamp[0] = sec;
-    req->transmit_timestamp[1] = frac;
-  }
-#endif /* SNTP_CHECK_RESPONSE >= 2 || SNTP_COMP_ROUNDTRIP */
-}
-
-/**
- * Retry: send a new request (and increase retry timeout).
- *
- * @param arg is unused (only necessary to conform to sys_timeout)
- */
-static void
-sntp_retry(void *arg)
-{
-  LWIP_UNUSED_ARG(arg);
-
-  LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_retry: Next request will be sent in %"U32_F" ms\n",
-                                 sntp_retry_timeout));
-
-  /* set up a timer to send a retry and increase the retry delay */
-  sys_timeout(sntp_retry_timeout, sntp_request, NULL);
-
-#if SNTP_RETRY_TIMEOUT_EXP
-  {
-    u32_t new_retry_timeout;
-    /* increase the timeout for next retry */
-    new_retry_timeout = sntp_retry_timeout << 1;
-    /* limit to maximum timeout and prevent overflow */
-    if ((new_retry_timeout <= SNTP_RETRY_TIMEOUT_MAX) &&
-        (new_retry_timeout > sntp_retry_timeout)) {
-      sntp_retry_timeout = new_retry_timeout;
-    }
-  }
-#endif /* SNTP_RETRY_TIMEOUT_EXP */
-}
-
-#if SNTP_SUPPORT_MULTIPLE_SERVERS
-/**
- * If Kiss-of-Death is received (or another packet parsing error),
- * try the next server or retry the current server and increase the retry
- * timeout if only one server is available.
- * (implicitly, SNTP_MAX_SERVERS > 1)
- *
- * @param arg is unused (only necessary to conform to sys_timeout)
- */
-static void
-sntp_try_next_server(void *arg)
-{
-  u8_t old_server, i;
-  LWIP_UNUSED_ARG(arg);
-
-  old_server = sntp_current_server;
-  for (i = 0; i < SNTP_MAX_SERVERS - 1; i++) {
-    sntp_current_server++;
-    if (sntp_current_server >= SNTP_MAX_SERVERS) {
-      sntp_current_server = 0;
-    }
-    if (!ip_addr_isany(&sntp_servers[sntp_current_server].addr)
-#if SNTP_SERVER_DNS
-        || (sntp_servers[sntp_current_server].name != NULL)
-#endif
-       ) {
-      LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_try_next_server: Sending request to server %"U16_F"\n",
-                                     (u16_t)sntp_current_server));
-      /* new server: reset retry timeout */
-      SNTP_RESET_RETRY_TIMEOUT();
-      /* instantly send a request to the next server */
-      sntp_request(NULL);
-      return;
-    }
-  }
-  /* no other valid server found */
-  sntp_current_server = old_server;
-  sntp_retry(NULL);
-}
-#else /* SNTP_SUPPORT_MULTIPLE_SERVERS */
-/* Always retry on error if only one server is supported */
-#define sntp_try_next_server    sntp_retry
-#endif /* SNTP_SUPPORT_MULTIPLE_SERVERS */
-
-/** UDP recv callback for the sntp pcb */
-static void
-sntp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
-{
-  struct sntp_timestamps timestamps;
-  u8_t mode;
-  u8_t stratum;
-  err_t err;
-
-  LWIP_UNUSED_ARG(arg);
-  LWIP_UNUSED_ARG(pcb);
-
-  err = ERR_ARG;
-#if SNTP_CHECK_RESPONSE >= 1
-  /* check server address and port */
-  if (((sntp_opmode != SNTP_OPMODE_POLL) || ip_addr_cmp(addr, &sntp_last_server_address)) &&
-      (port == SNTP_PORT))
-#else /* SNTP_CHECK_RESPONSE >= 1 */
-  LWIP_UNUSED_ARG(addr);
-  LWIP_UNUSED_ARG(port);
-#endif /* SNTP_CHECK_RESPONSE >= 1 */
-  {
-    /* process the response */
-    if (p->tot_len == SNTP_MSG_LEN) {
-      mode = pbuf_get_at(p, SNTP_OFFSET_LI_VN_MODE) & SNTP_MODE_MASK;
-      /* if this is a SNTP response... */
-      if (((sntp_opmode == SNTP_OPMODE_POLL)       && (mode == SNTP_MODE_SERVER)) ||
-          ((sntp_opmode == SNTP_OPMODE_LISTENONLY) && (mode == SNTP_MODE_BROADCAST))) {
-        stratum = pbuf_get_at(p, SNTP_OFFSET_STRATUM);
-
-        if (stratum == SNTP_STRATUM_KOD) {
-          /* Kiss-of-death packet. Use another server or increase UPDATE_DELAY. */
-          err = SNTP_ERR_KOD;
-          LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_recv: Received Kiss-of-Death\n"));
-        } else {
-          pbuf_copy_partial(p, &timestamps, sizeof(timestamps), SNTP_OFFSET_TIMESTAMPS);
-#if SNTP_CHECK_RESPONSE >= 2
-          /* check originate_timetamp against sntp_last_timestamp_sent */
-          if (timestamps.orig.sec != sntp_last_timestamp_sent.sec ||
-              timestamps.orig.frac != sntp_last_timestamp_sent.frac) {
-            LWIP_DEBUGF(SNTP_DEBUG_WARN,
-                        ("sntp_recv: Invalid originate timestamp in response\n"));
-          } else
-#endif /* SNTP_CHECK_RESPONSE >= 2 */
-            /* @todo: add code for SNTP_CHECK_RESPONSE >= 3 and >= 4 here */
-          {
-            /* correct answer */
-            err = ERR_OK;
-          }
-        }
-      } else {
-        LWIP_DEBUGF(SNTP_DEBUG_WARN, ("sntp_recv: Invalid mode in response: %"U16_F"\n", (u16_t)mode));
-        /* wait for correct response */
-        err = ERR_TIMEOUT;
-      }
-    } else {
-      LWIP_DEBUGF(SNTP_DEBUG_WARN, ("sntp_recv: Invalid packet length: %"U16_F"\n", p->tot_len));
-    }
-  }
-#if SNTP_CHECK_RESPONSE >= 1
-  else {
-    /* packet from wrong remote address or port, wait for correct response */
-    err = ERR_TIMEOUT;
-  }
-#endif /* SNTP_CHECK_RESPONSE >= 1 */
-
-  pbuf_free(p);
-
-  if (err == ERR_OK) {
-    /* correct packet received: process it it */
-    sntp_process(&timestamps);
-
-#if SNTP_MONITOR_SERVER_REACHABILITY
-    /* indicate that server responded */
-    sntp_servers[sntp_current_server].reachability |= 1;
-#endif /* SNTP_MONITOR_SERVER_REACHABILITY */
-    /* Set up timeout for next request (only if poll response was received)*/
-    if (sntp_opmode == SNTP_OPMODE_POLL) {
-      u32_t sntp_update_delay;
-      sys_untimeout(sntp_try_next_server, NULL);
-      sys_untimeout(sntp_request, NULL);
-
-      /* Correct response, reset retry timeout */
-      SNTP_RESET_RETRY_TIMEOUT();
-
-      sntp_update_delay = (u32_t)SNTP_UPDATE_DELAY;
-      sys_timeout(sntp_update_delay, sntp_request, NULL);
-      LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_recv: Scheduled next time request: %"U32_F" ms\n",
-                                     sntp_update_delay));
-    }
-  } else if (err == SNTP_ERR_KOD) {
-    /* KOD errors are only processed in case of an explicit poll response */
-    if (sntp_opmode == SNTP_OPMODE_POLL) {
-      /* Kiss-of-death packet. Use another server or increase UPDATE_DELAY. */
-      sntp_try_next_server(NULL);
-    }
-  } else {
-    /* ignore any broken packet, poll mode: retry after timeout to avoid flooding */
-  }
-}
-
-/** Actually send an sntp request to a server.
- *
- * @param server_addr resolved IP address of the SNTP server
- */
-static void
-sntp_send_request(const ip_addr_t *server_addr)
-{
-  struct pbuf *p;
-
-  LWIP_ASSERT("server_addr != NULL", server_addr != NULL);
-
-  p = pbuf_alloc(PBUF_TRANSPORT, SNTP_MSG_LEN, PBUF_RAM);
-  if (p != NULL) {
-    struct sntp_msg *sntpmsg = (struct sntp_msg *)p->payload;
-    LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_send_request: Sending request to server\n"));
-    /* initialize request message */
-    sntp_initialize_request(sntpmsg);
-    /* send request */
-    udp_sendto(sntp_pcb, p, server_addr, SNTP_PORT);
-    /* free the pbuf after sending it */
-    pbuf_free(p);
-#if SNTP_MONITOR_SERVER_REACHABILITY
-    /* indicate new packet has been sent */
-    sntp_servers[sntp_current_server].reachability <<= 1;
-#endif /* SNTP_MONITOR_SERVER_REACHABILITY */
-    /* set up receive timeout: try next server or retry on timeout */
-    sys_timeout((u32_t)SNTP_RECV_TIMEOUT, sntp_try_next_server, NULL);
-#if SNTP_CHECK_RESPONSE >= 1
-    /* save server address to verify it in sntp_recv */
-    ip_addr_copy(sntp_last_server_address, *server_addr);
-#endif /* SNTP_CHECK_RESPONSE >= 1 */
-  } else {
-    LWIP_DEBUGF(SNTP_DEBUG_SERIOUS, ("sntp_send_request: Out of memory, trying again in %"U32_F" ms\n",
-                                     (u32_t)SNTP_RETRY_TIMEOUT));
-    /* out of memory: set up a timer to send a retry */
-    sys_timeout((u32_t)SNTP_RETRY_TIMEOUT, sntp_request, NULL);
-  }
-}
-
-#if SNTP_SERVER_DNS
-/**
- * DNS found callback when using DNS names as server address.
- */
-static void
-sntp_dns_found(const char *hostname, const ip_addr_t *ipaddr, void *arg)
-{
-  LWIP_UNUSED_ARG(hostname);
-  LWIP_UNUSED_ARG(arg);
-
-  if (ipaddr != NULL) {
-    /* Address resolved, send request */
-    LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_dns_found: Server address resolved, sending request\n"));
-    sntp_servers[sntp_current_server].addr = *ipaddr;
-    sntp_send_request(ipaddr);
-  } else {
-    /* DNS resolving failed -> try another server */
-    LWIP_DEBUGF(SNTP_DEBUG_WARN_STATE, ("sntp_dns_found: Failed to resolve server address resolved, trying next server\n"));
-    sntp_try_next_server(NULL);
-  }
-}
-#endif /* SNTP_SERVER_DNS */
-
-/**
- * Send out an sntp request.
- *
- * @param arg is unused (only necessary to conform to sys_timeout)
- */
-static void
-sntp_request(void *arg)
-{
-  ip_addr_t sntp_server_address;
-  err_t err;
-
-  LWIP_UNUSED_ARG(arg);
-
-  /* initialize SNTP server address */
-#if SNTP_SERVER_DNS
-  if (sntp_servers[sntp_current_server].name) {
-    /* always resolve the name and rely on dns-internal caching & timeout */
-    ip_addr_set_zero(&sntp_servers[sntp_current_server].addr);
-    err = dns_gethostbyname(sntp_servers[sntp_current_server].name, &sntp_server_address,
-                            sntp_dns_found, NULL);
-    if (err == ERR_INPROGRESS) {
-      /* DNS request sent, wait for sntp_dns_found being called */
-      LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_request: Waiting for server address to be resolved.\n"));
-      return;
-    } else if (err == ERR_OK) {
-      sntp_servers[sntp_current_server].addr = sntp_server_address;
-    }
-  } else
-#endif /* SNTP_SERVER_DNS */
-  {
-    sntp_server_address = sntp_servers[sntp_current_server].addr;
-    err = (ip_addr_isany_val(sntp_server_address)) ? ERR_ARG : ERR_OK;
-  }
-
-  if (err == ERR_OK) {
-    LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp_request: current server address is %s\n",
-                                   ipaddr_ntoa(&sntp_server_address)));
-    sntp_send_request(&sntp_server_address);
-  } else {
-    /* address conversion failed, try another server */
-    LWIP_DEBUGF(SNTP_DEBUG_WARN_STATE, ("sntp_request: Invalid server address, trying next server.\n"));
-    sys_timeout((u32_t)SNTP_RETRY_TIMEOUT, sntp_try_next_server, NULL);
-  }
-}
-
-/**
- * @ingroup sntp
- * Initialize this module.
- * Send out request instantly or after SNTP_STARTUP_DELAY(_FUNC).
- */
-void
-sntp_init(void)
-{
-  /* LWIP_ASSERT_CORE_LOCKED(); is checked by udp_new() */
-
-#ifdef SNTP_SERVER_ADDRESS
-#if SNTP_SERVER_DNS
-  sntp_setservername(0, SNTP_SERVER_ADDRESS);
-#else
-#error SNTP_SERVER_ADDRESS string not supported SNTP_SERVER_DNS==0
-#endif
-#endif /* SNTP_SERVER_ADDRESS */
-
-  if (sntp_pcb == NULL) {
-    sntp_pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
-    LWIP_ASSERT("Failed to allocate udp pcb for sntp client", sntp_pcb != NULL);
-    if (sntp_pcb != NULL) {
-      udp_recv(sntp_pcb, sntp_recv, NULL);
-
-      if (sntp_opmode == SNTP_OPMODE_POLL) {
-        SNTP_RESET_RETRY_TIMEOUT();
-#if SNTP_STARTUP_DELAY
-        sys_timeout((u32_t)SNTP_STARTUP_DELAY_FUNC, sntp_request, NULL);
-#else
-        sntp_request(NULL);
-#endif
-      } else if (sntp_opmode == SNTP_OPMODE_LISTENONLY) {
-        ip_set_option(sntp_pcb, SOF_BROADCAST);
-        udp_bind(sntp_pcb, IP_ANY_TYPE, SNTP_PORT);
-      }
-    }
-  }
-}
-
-/**
- * @ingroup sntp
- * Stop this module.
- */
-void
-sntp_stop(void)
-{
-  LWIP_ASSERT_CORE_LOCKED();
-  if (sntp_pcb != NULL) {
-#if SNTP_MONITOR_SERVER_REACHABILITY
-    u8_t i;
-    for (i = 0; i < SNTP_MAX_SERVERS; i++) {
-      sntp_servers[i].reachability = 0;
-    }
-#endif /* SNTP_MONITOR_SERVER_REACHABILITY */
-    sys_untimeout(sntp_request, NULL);
-    sys_untimeout(sntp_try_next_server, NULL);
-    udp_remove(sntp_pcb);
-    sntp_pcb = NULL;
-  }
-}
-
-/**
- * @ingroup sntp
- * Get enabled state.
- */
-u8_t sntp_enabled(void)
-{
-  return (sntp_pcb != NULL) ? 1 : 0;
-}
-
-/**
- * @ingroup sntp
- * Sets the operating mode.
- * @param operating_mode one of the available operating modes
- */
-void
-sntp_setoperatingmode(u8_t operating_mode)
-{
-  LWIP_ASSERT_CORE_LOCKED();
-  LWIP_ASSERT("Invalid operating mode", operating_mode <= SNTP_OPMODE_LISTENONLY);
-  LWIP_ASSERT("Operating mode must not be set while SNTP client is running", sntp_pcb == NULL);
-  sntp_opmode = operating_mode;
-}
-
-/**
- * @ingroup sntp
- * Gets the operating mode.
- */
-u8_t
-sntp_getoperatingmode(void)
-{
-  return sntp_opmode;
-}
-
-#if SNTP_MONITOR_SERVER_REACHABILITY
-/**
- * @ingroup sntp
- * Gets the server reachability shift register as described in RFC 5905.
- *
- * @param idx the index of the NTP server
- */
-u8_t
-sntp_getreachability(u8_t idx)
-{
-  if (idx < SNTP_MAX_SERVERS) {
-    return sntp_servers[idx].reachability;
-  }
-  return 0;
-}
-#endif /* SNTP_MONITOR_SERVER_REACHABILITY */
-
-#if SNTP_GET_SERVERS_FROM_DHCP
-/**
- * Config SNTP server handling by IP address, name, or DHCP; clear table
- * @param set_servers_from_dhcp enable or disable getting server addresses from dhcp
- */
-void
-sntp_servermode_dhcp(int set_servers_from_dhcp)
-{
-  u8_t new_mode = set_servers_from_dhcp ? 1 : 0;
-  LWIP_ASSERT_CORE_LOCKED();
-  if (sntp_set_servers_from_dhcp != new_mode) {
-    sntp_set_servers_from_dhcp = new_mode;
-  }
-}
-#endif /* SNTP_GET_SERVERS_FROM_DHCP */
-
-/**
- * @ingroup sntp
- * Initialize one of the NTP servers by IP address
- *
- * @param idx the index of the NTP server to set must be < SNTP_MAX_SERVERS
- * @param server IP address of the NTP server to set
- */
-void
-sntp_setserver(u8_t idx, const ip_addr_t *server)
-{
-  LWIP_ASSERT_CORE_LOCKED();
-  if (idx < SNTP_MAX_SERVERS) {
-    if (server != NULL) {
-      sntp_servers[idx].addr = (*server);
-    } else {
-      ip_addr_set_zero(&sntp_servers[idx].addr);
-    }
-#if SNTP_SERVER_DNS
-    sntp_servers[idx].name = NULL;
-#endif
-  }
-}
-
-#if LWIP_DHCP && SNTP_GET_SERVERS_FROM_DHCP
-/**
- * Initialize one of the NTP servers by IP address, required by DHCP
- *
- * @param num the index of the NTP server to set must be < SNTP_MAX_SERVERS
- * @param server IP address of the NTP server to set
- */
-void
-dhcp_set_ntp_servers(u8_t num, const ip4_addr_t *server)
-{
-  LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp: %s %u.%u.%u.%u as NTP server #%u via DHCP\n",
-                                 (sntp_set_servers_from_dhcp ? "Got" : "Rejected"),
-                                 ip4_addr1(server), ip4_addr2(server), ip4_addr3(server), ip4_addr4(server), num));
-  if (sntp_set_servers_from_dhcp && num) {
-    u8_t i;
-    for (i = 0; (i < num) && (i < SNTP_MAX_SERVERS); i++) {
-      ip_addr_t addr;
-      ip_addr_copy_from_ip4(addr, server[i]);
-      sntp_setserver(i, &addr);
-    }
-    for (i = num; i < SNTP_MAX_SERVERS; i++) {
-      sntp_setserver(i, NULL);
-    }
-  }
-}
-#endif /* LWIP_DHCP && SNTP_GET_SERVERS_FROM_DHCP */
-
-/**
- * @ingroup sntp
- * Obtain one of the currently configured by IP address (or DHCP) NTP servers
- *
- * @param idx the index of the NTP server
- * @return IP address of the indexed NTP server or "ip_addr_any" if the NTP
- *         server has not been configured by address (or at all).
- */
-const ip_addr_t *
-sntp_getserver(u8_t idx)
-{
-  if (idx < SNTP_MAX_SERVERS) {
-    return &sntp_servers[idx].addr;
-  }
-  return IP_ADDR_ANY;
-}
-
-#if SNTP_SERVER_DNS
-/**
- * Initialize one of the NTP servers by name
- *
- * @param idx the index of the NTP server to set must be < SNTP_MAX_SERVERS
- * @param server DNS name of the NTP server to set, to be resolved at contact time
- */
-void
-sntp_setservername(u8_t idx, const char *server)
-{
-  LWIP_ASSERT_CORE_LOCKED();
-  if (idx < SNTP_MAX_SERVERS) {
-    sntp_servers[idx].name = server;
-  }
-}
-
-/**
- * Obtain one of the currently configured by name NTP servers.
- *
- * @param idx the index of the NTP server
- * @return IP address of the indexed NTP server or NULL if the NTP
- *         server has not been configured by name (or at all)
- */
-const char *
-sntp_getservername(u8_t idx)
-{
-  if (idx < SNTP_MAX_SERVERS) {
-    return sntp_servers[idx].name;
-  }
-  return NULL;
-}
-#endif /* SNTP_SERVER_DNS */
-
-#endif /* LWIP_UDP */
+/**
+ * @file
+ * SNTP client module
+ */
+
+/*
+ * Copyright (c) 2007-2009 Frédéric Bernon, Simon Goldschmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Frédéric Bernon, Simon Goldschmidt
+ */
+
+
+/**
+ * @defgroup sntp SNTP
+ * @ingroup apps
+ *
+ * This is simple "SNTP" client for the lwIP raw API.
+ * It is a minimal implementation of SNTPv4 as specified in RFC 4330.
+ *
+ * For a list of some public NTP servers, see this link :
+ * http://support.ntp.org/bin/view/Servers/NTPPoolServers
+ *
+ * @todo:
+ * - set/change servers at runtime
+ * - complete SNTP_CHECK_RESPONSE checks 3 and 4
+ */
+
+#include "lwip/apps/sntp.h"
+
+#include "lwip/opt.h"
+#include "lwip/timeouts.h"
+#include "lwip/udp.h"
+#include "lwip/dns.h"
+#include "lwip/ip_addr.h"
+#include "lwip/pbuf.h"
+#include "lwip/dhcp.h"
+
+#include <string.h>
+#include <time.h>
+
+#if LWIP_UDP
+
+/* Handle support for more than one server via SNTP_MAX_SERVERS */
+#if SNTP_MAX_SERVERS > 1
+#define SNTP_SUPPORT_MULTIPLE_SERVERS 1
+#else /* NTP_MAX_SERVERS > 1 */
+#define SNTP_SUPPORT_MULTIPLE_SERVERS 0
+#endif /* NTP_MAX_SERVERS > 1 */
+
+#if (SNTP_UPDATE_DELAY < 15000) && !defined(SNTP_SUPPRESS_DELAY_CHECK)
+#error "SNTPv4 RFC 4330 enforces a minimum update time of 15 seconds (define SNTP_SUPPRESS_DELAY_CHECK to disable this error)!"
+#endif
+
+/* Configure behaviour depending on microsecond or second precision */
+#ifdef SNTP_SET_SYSTEM_TIME_US
+#define SNTP_CALC_TIME_US           1
+#define SNTP_RECEIVE_TIME_SIZE      2
+#else
+#define SNTP_SET_SYSTEM_TIME_US(sec, us)
+#define SNTP_CALC_TIME_US           0
+#define SNTP_RECEIVE_TIME_SIZE      1
+#endif
+
+
+/* the various debug levels for this file */
+#define SNTP_DEBUG_TRACE        (SNTP_DEBUG | LWIP_DBG_TRACE)
+#define SNTP_DEBUG_STATE        (SNTP_DEBUG | LWIP_DBG_STATE)
+#define SNTP_DEBUG_WARN         (SNTP_DEBUG | LWIP_DBG_LEVEL_WARNING)
+#define SNTP_DEBUG_WARN_STATE   (SNTP_DEBUG | LWIP_DBG_LEVEL_WARNING | LWIP_DBG_STATE)
+#define SNTP_DEBUG_SERIOUS      (SNTP_DEBUG | LWIP_DBG_LEVEL_SERIOUS)
+
+#define SNTP_ERR_KOD                1
+
+/* SNTP protocol defines */
+#define SNTP_MSG_LEN                48
+
+#define SNTP_OFFSET_LI_VN_MODE      0
+#define SNTP_LI_MASK                0xC0
+#define SNTP_LI_NO_WARNING          0x00
+#define SNTP_LI_LAST_MINUTE_61_SEC  0x01
+#define SNTP_LI_LAST_MINUTE_59_SEC  0x02
+#define SNTP_LI_ALARM_CONDITION     0x03 /* (clock not synchronized) */
+
+#define SNTP_VERSION_MASK           0x38
+#define SNTP_VERSION                (4/* NTP Version 4*/<<3)
+
+#define SNTP_MODE_MASK              0x07
+#define SNTP_MODE_CLIENT            0x03
+#define SNTP_MODE_SERVER            0x04
+#define SNTP_MODE_BROADCAST         0x05
+
+#define SNTP_OFFSET_STRATUM         1
+#define SNTP_STRATUM_KOD            0x00
+
+#define SNTP_OFFSET_ORIGINATE_TIME  24
+#define SNTP_OFFSET_RECEIVE_TIME    32
+#define SNTP_OFFSET_TRANSMIT_TIME   40
+
+/* number of seconds between 1900 and 1970 (MSB=1)*/
+#define DIFF_SEC_1900_1970         (2208988800UL)
+/* number of seconds between 1970 and Feb 7, 2036 (6:28:16 UTC) (MSB=0) */
+#define DIFF_SEC_1970_2036         (2085978496UL)
+
+/**
+ * SNTP packet format (without optional fields)
+ * Timestamps are coded as 64 bits:
+ * - 32 bits seconds since Jan 01, 1970, 00:00
+ * - 32 bits seconds fraction (0-padded)
+ * For future use, if the MSB in the seconds part is set, seconds are based
+ * on Feb 07, 2036, 06:28:16.
+ */
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct sntp_msg {
+  PACK_STRUCT_FLD_8(u8_t  li_vn_mode);
+  PACK_STRUCT_FLD_8(u8_t  stratum);
+  PACK_STRUCT_FLD_8(u8_t  poll);
+  PACK_STRUCT_FLD_8(u8_t  precision);
+  PACK_STRUCT_FIELD(u32_t root_delay);
+  PACK_STRUCT_FIELD(u32_t root_dispersion);
+  PACK_STRUCT_FIELD(u32_t reference_identifier);
+  PACK_STRUCT_FIELD(u32_t reference_timestamp[2]);
+  PACK_STRUCT_FIELD(u32_t originate_timestamp[2]);
+  PACK_STRUCT_FIELD(u32_t receive_timestamp[2]);
+  PACK_STRUCT_FIELD(u32_t transmit_timestamp[2]);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+/* function prototypes */
+static void sntp_request(void *arg);
+
+/** The operating mode */
+static u8_t sntp_opmode;
+
+/** The UDP pcb used by the SNTP client */
+static struct udp_pcb* sntp_pcb;
+/** Names/Addresses of servers */
+struct sntp_server {
+#if SNTP_SERVER_DNS
+  char* name;
+#endif /* SNTP_SERVER_DNS */
+  ip_addr_t addr;
+};
+static struct sntp_server sntp_servers[SNTP_MAX_SERVERS];
+
+#if SNTP_GET_SERVERS_FROM_DHCP
+static u8_t sntp_set_servers_from_dhcp;
+#endif /* SNTP_GET_SERVERS_FROM_DHCP */
+#if SNTP_SUPPORT_MULTIPLE_SERVERS
+/** The currently used server (initialized to 0) */
+static u8_t sntp_current_server;
+#else /* SNTP_SUPPORT_MULTIPLE_SERVERS */
+#define sntp_current_server 0
+#endif /* SNTP_SUPPORT_MULTIPLE_SERVERS */
+
+#if SNTP_RETRY_TIMEOUT_EXP
+#define SNTP_RESET_RETRY_TIMEOUT() sntp_retry_timeout = SNTP_RETRY_TIMEOUT
+/** Retry time, initialized with SNTP_RETRY_TIMEOUT and doubled with each retry. */
+static u32_t sntp_retry_timeout;
+#else /* SNTP_RETRY_TIMEOUT_EXP */
+#define SNTP_RESET_RETRY_TIMEOUT()
+#define sntp_retry_timeout SNTP_RETRY_TIMEOUT
+#endif /* SNTP_RETRY_TIMEOUT_EXP */
+
+#if SNTP_CHECK_RESPONSE >= 1
+/** Saves the last server address to compare with response */
+static ip_addr_t sntp_last_server_address;
+#endif /* SNTP_CHECK_RESPONSE >= 1 */
+
+#if SNTP_CHECK_RESPONSE >= 2
+/** Saves the last timestamp sent (which is sent back by the server)
+ * to compare against in response */
+static u32_t sntp_last_timestamp_sent[2];
+#endif /* SNTP_CHECK_RESPONSE >= 2 */
+
+/**
+ * SNTP processing of received timestamp
+ */
+static void
+sntp_process(u32_t *receive_timestamp)
+{
+  /* convert SNTP time (1900-based) to unix GMT time (1970-based)
+   * if MSB is 0, SNTP time is 2036-based!
+   */
+  u32_t rx_secs = lwip_ntohl(receive_timestamp[0]);
+  int is_1900_based = ((rx_secs & 0x80000000) != 0);
+  u32_t t = is_1900_based ? (rx_secs - DIFF_SEC_1900_1970) : (rx_secs + DIFF_SEC_1970_2036);
+  time_t tim = t;
+
+#if SNTP_CALC_TIME_US
+  u32_t us = lwip_ntohl(receive_timestamp[1]) / 4295;
+  SNTP_SET_SYSTEM_TIME_US(t, us);
+  /* display local time from GMT time */
+  LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp_process: %s, %"U32_F" us", ctime(&tim), us));
+
+#else /* SNTP_CALC_TIME_US */
+
+  /* change system time and/or the update the RTC clock */
+  SNTP_SET_SYSTEM_TIME(t);
+  /* display local time from GMT time */
+  LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp_process: %s", ctime(&tim)));
+#endif /* SNTP_CALC_TIME_US */
+  LWIP_UNUSED_ARG(tim);
+}
+
+/**
+ * Initialize request struct to be sent to server.
+ */
+static void
+sntp_initialize_request(struct sntp_msg *req)
+{
+  memset(req, 0, SNTP_MSG_LEN);
+  req->li_vn_mode = SNTP_LI_NO_WARNING | SNTP_VERSION | SNTP_MODE_CLIENT;
+
+#if SNTP_CHECK_RESPONSE >= 2
+  {
+    u32_t sntp_time_sec, sntp_time_us;
+    /* fill in transmit timestamp and save it in 'sntp_last_timestamp_sent' */
+    SNTP_GET_SYSTEM_TIME(sntp_time_sec, sntp_time_us);
+    sntp_last_timestamp_sent[0] = lwip_htonl(sntp_time_sec + DIFF_SEC_1900_1970);
+    req->transmit_timestamp[0] = sntp_last_timestamp_sent[0];
+    /* we send/save us instead of fraction to be faster... */
+    sntp_last_timestamp_sent[1] = lwip_htonl(sntp_time_us);
+    req->transmit_timestamp[1] = sntp_last_timestamp_sent[1];
+  }
+#endif /* SNTP_CHECK_RESPONSE >= 2 */
+}
+
+/**
+ * Retry: send a new request (and increase retry timeout).
+ *
+ * @param arg is unused (only necessary to conform to sys_timeout)
+ */
+static void
+sntp_retry(void* arg)
+{
+  LWIP_UNUSED_ARG(arg);
+
+  LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_retry: Next request will be sent in %"U32_F" ms\n",
+    sntp_retry_timeout));
+
+  /* set up a timer to send a retry and increase the retry delay */
+  sys_timeout(sntp_retry_timeout, sntp_request, NULL);
+
+#if SNTP_RETRY_TIMEOUT_EXP
+  {
+    u32_t new_retry_timeout;
+    /* increase the timeout for next retry */
+    new_retry_timeout = sntp_retry_timeout << 1;
+    /* limit to maximum timeout and prevent overflow */
+    if ((new_retry_timeout <= SNTP_RETRY_TIMEOUT_MAX) &&
+        (new_retry_timeout > sntp_retry_timeout)) {
+      sntp_retry_timeout = new_retry_timeout;
+    }
+  }
+#endif /* SNTP_RETRY_TIMEOUT_EXP */
+}
+
+#if SNTP_SUPPORT_MULTIPLE_SERVERS
+/**
+ * If Kiss-of-Death is received (or another packet parsing error),
+ * try the next server or retry the current server and increase the retry
+ * timeout if only one server is available.
+ * (implicitly, SNTP_MAX_SERVERS > 1)
+ *
+ * @param arg is unused (only necessary to conform to sys_timeout)
+ */
+static void
+sntp_try_next_server(void* arg)
+{
+  u8_t old_server, i;
+  LWIP_UNUSED_ARG(arg);
+
+  old_server = sntp_current_server;
+  for (i = 0; i < SNTP_MAX_SERVERS - 1; i++) {
+    sntp_current_server++;
+    if (sntp_current_server >= SNTP_MAX_SERVERS) {
+      sntp_current_server = 0;
+    }
+    if (!ip_addr_isany(&sntp_servers[sntp_current_server].addr)
+#if SNTP_SERVER_DNS
+        || (sntp_servers[sntp_current_server].name != NULL)
+#endif
+        ) {
+      LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_try_next_server: Sending request to server %"U16_F"\n",
+        (u16_t)sntp_current_server));
+      /* new server: reset retry timeout */
+      SNTP_RESET_RETRY_TIMEOUT();
+      /* instantly send a request to the next server */
+      sntp_request(NULL);
+      return;
+    }
+  }
+  /* no other valid server found */
+  sntp_current_server = old_server;
+  sntp_retry(NULL);
+}
+#else /* SNTP_SUPPORT_MULTIPLE_SERVERS */
+/* Always retry on error if only one server is supported */
+#define sntp_try_next_server    sntp_retry
+#endif /* SNTP_SUPPORT_MULTIPLE_SERVERS */
+
+/** UDP recv callback for the sntp pcb */
+static void
+sntp_recv(void *arg, struct udp_pcb* pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
+{
+  u8_t mode;
+  u8_t stratum;
+  u32_t receive_timestamp[SNTP_RECEIVE_TIME_SIZE];
+  err_t err;
+
+  LWIP_UNUSED_ARG(arg);
+  LWIP_UNUSED_ARG(pcb);
+
+  /* packet received: stop retry timeout  */
+  sys_untimeout(sntp_try_next_server, NULL);
+  sys_untimeout(sntp_request, NULL);
+
+  err = ERR_ARG;
+#if SNTP_CHECK_RESPONSE >= 1
+  /* check server address and port */
+  if (((sntp_opmode != SNTP_OPMODE_POLL) || ip_addr_cmp(addr, &sntp_last_server_address)) &&
+    (port == SNTP_PORT))
+#else /* SNTP_CHECK_RESPONSE >= 1 */
+  LWIP_UNUSED_ARG(addr);
+  LWIP_UNUSED_ARG(port);
+#endif /* SNTP_CHECK_RESPONSE >= 1 */
+  {
+    /* process the response */
+    if (p->tot_len == SNTP_MSG_LEN) {
+      pbuf_copy_partial(p, &mode, 1, SNTP_OFFSET_LI_VN_MODE);
+      mode &= SNTP_MODE_MASK;
+      /* if this is a SNTP response... */
+      if (((sntp_opmode == SNTP_OPMODE_POLL) && (mode == SNTP_MODE_SERVER)) ||
+          ((sntp_opmode == SNTP_OPMODE_LISTENONLY) && (mode == SNTP_MODE_BROADCAST))) {
+        pbuf_copy_partial(p, &stratum, 1, SNTP_OFFSET_STRATUM);
+        if (stratum == SNTP_STRATUM_KOD) {
+          /* Kiss-of-death packet. Use another server or increase UPDATE_DELAY. */
+          err = SNTP_ERR_KOD;
+          LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_recv: Received Kiss-of-Death\n"));
+        } else {
+#if SNTP_CHECK_RESPONSE >= 2
+          /* check originate_timetamp against sntp_last_timestamp_sent */
+          u32_t originate_timestamp[2];
+          pbuf_copy_partial(p, &originate_timestamp, 8, SNTP_OFFSET_ORIGINATE_TIME);
+          if ((originate_timestamp[0] != sntp_last_timestamp_sent[0]) ||
+              (originate_timestamp[1] != sntp_last_timestamp_sent[1]))
+          {
+            LWIP_DEBUGF(SNTP_DEBUG_WARN, ("sntp_recv: Invalid originate timestamp in response\n"));
+          } else
+#endif /* SNTP_CHECK_RESPONSE >= 2 */
+          /* @todo: add code for SNTP_CHECK_RESPONSE >= 3 and >= 4 here */
+          {
+            /* correct answer */
+            err = ERR_OK;
+            pbuf_copy_partial(p, &receive_timestamp, SNTP_RECEIVE_TIME_SIZE * 4, SNTP_OFFSET_TRANSMIT_TIME);
+          }
+        }
+      } else {
+        LWIP_DEBUGF(SNTP_DEBUG_WARN, ("sntp_recv: Invalid mode in response: %"U16_F"\n", (u16_t)mode));
+        /* wait for correct response */
+        err = ERR_TIMEOUT;
+      }
+    } else {
+      LWIP_DEBUGF(SNTP_DEBUG_WARN, ("sntp_recv: Invalid packet length: %"U16_F"\n", p->tot_len));
+    }
+  }
+#if SNTP_CHECK_RESPONSE >= 1
+  else {
+    /* packet from wrong remote address or port, wait for correct response */
+    err = ERR_TIMEOUT;
+  }
+#endif /* SNTP_CHECK_RESPONSE >= 1 */
+  pbuf_free(p);
+  if (err == ERR_OK) {
+    sntp_process(receive_timestamp);
+
+    /* Set up timeout for next request (only if poll response was received)*/
+    if (sntp_opmode == SNTP_OPMODE_POLL) {
+      /* Correct response, reset retry timeout */
+      SNTP_RESET_RETRY_TIMEOUT();
+
+      sys_timeout((u32_t)SNTP_UPDATE_DELAY, sntp_request, NULL);
+      LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_recv: Scheduled next time request: %"U32_F" ms\n",
+        (u32_t)SNTP_UPDATE_DELAY));
+    }
+  } else if (err != ERR_TIMEOUT) {
+    /* Errors are only processed in case of an explicit poll response */
+    if (sntp_opmode == SNTP_OPMODE_POLL) {
+      if (err == SNTP_ERR_KOD) {
+        /* Kiss-of-death packet. Use another server or increase UPDATE_DELAY. */
+        sntp_try_next_server(NULL);
+      } else {
+        /* another error, try the same server again */
+        sntp_retry(NULL);
+      }
+    }
+  }
+}
+
+/** Actually send an sntp request to a server.
+ *
+ * @param server_addr resolved IP address of the SNTP server
+ */
+static void
+sntp_send_request(const ip_addr_t *server_addr)
+{
+  struct pbuf* p;
+  p = pbuf_alloc(PBUF_TRANSPORT, SNTP_MSG_LEN, PBUF_RAM);
+  if (p != NULL) {
+    struct sntp_msg *sntpmsg = (struct sntp_msg *)p->payload;
+    LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_send_request: Sending request to server\n"));
+    /* initialize request message */
+    sntp_initialize_request(sntpmsg);
+    /* send request */
+    udp_sendto(sntp_pcb, p, server_addr, SNTP_PORT);
+    /* free the pbuf after sending it */
+    pbuf_free(p);
+    /* set up receive timeout: try next server or retry on timeout */
+    sys_timeout((u32_t)SNTP_RECV_TIMEOUT, sntp_try_next_server, NULL);
+#if SNTP_CHECK_RESPONSE >= 1
+    /* save server address to verify it in sntp_recv */
+    ip_addr_set(&sntp_last_server_address, server_addr);
+#endif /* SNTP_CHECK_RESPONSE >= 1 */
+  } else {
+    LWIP_DEBUGF(SNTP_DEBUG_SERIOUS, ("sntp_send_request: Out of memory, trying again in %"U32_F" ms\n",
+      (u32_t)SNTP_RETRY_TIMEOUT));
+    /* out of memory: set up a timer to send a retry */
+    sys_timeout((u32_t)SNTP_RETRY_TIMEOUT, sntp_request, NULL);
+  }
+}
+
+#if SNTP_SERVER_DNS
+/**
+ * DNS found callback when using DNS names as server address.
+ */
+static void
+sntp_dns_found(const char* hostname, const ip_addr_t *ipaddr, void *arg)
+{
+  LWIP_UNUSED_ARG(hostname);
+  LWIP_UNUSED_ARG(arg);
+
+  if (ipaddr != NULL) {
+    /* Address resolved, send request */
+    LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_dns_found: Server address resolved, sending request\n"));
+    sntp_send_request(ipaddr);
+  } else {
+    /* DNS resolving failed -> try another server */
+    LWIP_DEBUGF(SNTP_DEBUG_WARN_STATE, ("sntp_dns_found: Failed to resolve server address resolved, trying next server\n"));
+    sntp_try_next_server(NULL);
+  }
+}
+#endif /* SNTP_SERVER_DNS */
+
+/**
+ * Send out an sntp request.
+ *
+ * @param arg is unused (only necessary to conform to sys_timeout)
+ */
+static void
+sntp_request(void *arg)
+{
+  ip_addr_t sntp_server_address;
+  err_t err;
+
+  LWIP_UNUSED_ARG(arg);
+
+  /* initialize SNTP server address */
+#if SNTP_SERVER_DNS
+  if (sntp_servers[sntp_current_server].name) {
+    /* always resolve the name and rely on dns-internal caching & timeout */
+    ip_addr_set_zero(&sntp_servers[sntp_current_server].addr);
+    err = dns_gethostbyname(sntp_servers[sntp_current_server].name, &sntp_server_address,
+      sntp_dns_found, NULL);
+    if (err == ERR_INPROGRESS) {
+      /* DNS request sent, wait for sntp_dns_found being called */
+      LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_request: Waiting for server address to be resolved.\n"));
+      return;
+    } else if (err == ERR_OK) {
+      sntp_servers[sntp_current_server].addr = sntp_server_address;
+    }
+  } else
+#endif /* SNTP_SERVER_DNS */
+  {
+    sntp_server_address = sntp_servers[sntp_current_server].addr;
+    err = (ip_addr_isany_val(sntp_server_address)) ? ERR_ARG : ERR_OK;
+  }
+
+  if (err == ERR_OK) {
+    LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp_request: current server address is %s\n",
+      ipaddr_ntoa(&sntp_server_address)));
+    sntp_send_request(&sntp_server_address);
+  } else {
+    /* address conversion failed, try another server */
+    LWIP_DEBUGF(SNTP_DEBUG_WARN_STATE, ("sntp_request: Invalid server address, trying next server.\n"));
+    sys_timeout((u32_t)SNTP_RETRY_TIMEOUT, sntp_try_next_server, NULL);
+  }
+}
+
+/**
+ * @ingroup sntp
+ * Initialize this module.
+ * Send out request instantly or after SNTP_STARTUP_DELAY(_FUNC).
+ */
+void
+sntp_init(void)
+{
+#ifdef SNTP_SERVER_ADDRESS
+#if SNTP_SERVER_DNS
+  sntp_setservername(0, SNTP_SERVER_ADDRESS);
+#else
+#error SNTP_SERVER_ADDRESS string not supported SNTP_SERVER_DNS==0
+#endif
+#endif /* SNTP_SERVER_ADDRESS */
+
+  if (sntp_pcb == NULL) {
+    sntp_pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
+    LWIP_ASSERT("Failed to allocate udp pcb for sntp client", sntp_pcb != NULL);
+    if (sntp_pcb != NULL) {
+      udp_recv(sntp_pcb, sntp_recv, NULL);
+
+      if (sntp_opmode == SNTP_OPMODE_POLL) {
+        SNTP_RESET_RETRY_TIMEOUT();
+#if SNTP_STARTUP_DELAY
+        sys_timeout((u32_t)SNTP_STARTUP_DELAY_FUNC, sntp_request, NULL);
+#else
+        sntp_request(NULL);
+#endif
+      } else if (sntp_opmode == SNTP_OPMODE_LISTENONLY) {
+        ip_set_option(sntp_pcb, SOF_BROADCAST);
+        udp_bind(sntp_pcb, IP_ANY_TYPE, SNTP_PORT);
+      }
+    }
+  }
+}
+
+/**
+ * @ingroup sntp
+ * Stop this module.
+ */
+void
+sntp_stop(void)
+{
+  if (sntp_pcb != NULL) {
+    sys_untimeout(sntp_request, NULL);
+    sys_untimeout(sntp_try_next_server, NULL);
+    udp_remove(sntp_pcb);
+    sntp_pcb = NULL;
+  }
+}
+
+/**
+ * @ingroup sntp
+ * Get enabled state.
+ */
+u8_t sntp_enabled(void)
+{
+  return (sntp_pcb != NULL)? 1 : 0;
+}
+
+/**
+ * @ingroup sntp
+ * Sets the operating mode.
+ * @param operating_mode one of the available operating modes
+ */
+void
+sntp_setoperatingmode(u8_t operating_mode)
+{
+  LWIP_ASSERT("Invalid operating mode", operating_mode <= SNTP_OPMODE_LISTENONLY);
+  LWIP_ASSERT("Operating mode must not be set while SNTP client is running", sntp_pcb == NULL);
+  sntp_opmode = operating_mode;
+}
+
+/**
+ * @ingroup sntp
+ * Gets the operating mode.
+ */
+u8_t
+sntp_getoperatingmode(void)
+{
+  return sntp_opmode;
+}
+
+#if SNTP_GET_SERVERS_FROM_DHCP
+/**
+ * Config SNTP server handling by IP address, name, or DHCP; clear table
+ * @param set_servers_from_dhcp enable or disable getting server addresses from dhcp
+ */
+void
+sntp_servermode_dhcp(int set_servers_from_dhcp)
+{
+  u8_t new_mode = set_servers_from_dhcp ? 1 : 0;
+  if (sntp_set_servers_from_dhcp != new_mode) {
+    sntp_set_servers_from_dhcp = new_mode;
+  }
+}
+#endif /* SNTP_GET_SERVERS_FROM_DHCP */
+
+/**
+ * @ingroup sntp
+ * Initialize one of the NTP servers by IP address
+ *
+ * @param idx the index of the NTP server to set must be < SNTP_MAX_SERVERS
+ * @param server IP address of the NTP server to set
+ */
+void
+sntp_setserver(u8_t idx, const ip_addr_t *server)
+{
+  if (idx < SNTP_MAX_SERVERS) {
+    if (server != NULL) {
+      sntp_servers[idx].addr = (*server);
+    } else {
+      ip_addr_set_zero(&sntp_servers[idx].addr);
+    }
+#if SNTP_SERVER_DNS
+    sntp_servers[idx].name = NULL;
+#endif
+  }
+}
+
+#if LWIP_DHCP && SNTP_GET_SERVERS_FROM_DHCP
+/**
+ * Initialize one of the NTP servers by IP address, required by DHCP
+ *
+ * @param numdns the index of the NTP server to set must be < SNTP_MAX_SERVERS
+ * @param dnsserver IP address of the NTP server to set
+ */
+void
+dhcp_set_ntp_servers(u8_t num, const ip4_addr_t *server)
+{
+  LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp: %s %u.%u.%u.%u as NTP server #%u via DHCP\n",
+    (sntp_set_servers_from_dhcp ? "Got" : "Rejected"),
+    ip4_addr1(server), ip4_addr2(server), ip4_addr3(server), ip4_addr4(server), num));
+  if (sntp_set_servers_from_dhcp && num) {
+    u8_t i;
+    for (i = 0; (i < num) && (i < SNTP_MAX_SERVERS); i++) {
+      ip_addr_t addr;
+      ip_addr_copy_from_ip4(addr, server[i]);
+      sntp_setserver(i, &addr);
+    }
+    for (i = num; i < SNTP_MAX_SERVERS; i++) {
+      sntp_setserver(i, NULL);
+    }
+  }
+}
+#endif /* LWIP_DHCP && SNTP_GET_SERVERS_FROM_DHCP */
+
+/**
+ * @ingroup sntp
+ * Obtain one of the currently configured by IP address (or DHCP) NTP servers
+ *
+ * @param idx the index of the NTP server
+ * @return IP address of the indexed NTP server or "ip_addr_any" if the NTP
+ *         server has not been configured by address (or at all).
+ */
+const ip_addr_t*
+sntp_getserver(u8_t idx)
+{
+  if (idx < SNTP_MAX_SERVERS) {
+    return &sntp_servers[idx].addr;
+  }
+  return IP_ADDR_ANY;
+}
+
+#if SNTP_SERVER_DNS
+/**
+ * Initialize one of the NTP servers by name
+ *
+ * @param numdns the index of the NTP server to set must be < SNTP_MAX_SERVERS
+ * @param dnsserver DNS name of the NTP server to set, to be resolved at contact time
+ */
+void
+sntp_setservername(u8_t idx, char *server)
+{
+  if (idx < SNTP_MAX_SERVERS) {
+    sntp_servers[idx].name = server;
+  }
+}
+
+/**
+ * Obtain one of the currently configured by name NTP servers.
+ *
+ * @param numdns the index of the NTP server
+ * @return IP address of the indexed NTP server or NULL if the NTP
+ *         server has not been configured by name (or at all)
+ */
+char *
+sntp_getservername(u8_t idx)
+{
+  if (idx < SNTP_MAX_SERVERS) {
+    return sntp_servers[idx].name;
+  }
+  return NULL;
+}
+#endif /* SNTP_SERVER_DNS */
+
+#endif /* LWIP_UDP */

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels