From 225b1894f9c1268366f55d37c5e160ea2a6c33a3 Mon Sep 17 00:00:00 2001 From: wh201906 Date: Mon, 23 Oct 2023 01:48:38 +0800 Subject: [PATCH 1/3] Add bind option (UDP, Linux) --- client/src/uart/uart.h | 4 ++ client/src/uart/uart_posix.c | 106 ++++++++++++++++++++++++++++++++++- client/src/uart/uart_win32.c | 4 ++ 3 files changed, 112 insertions(+), 2 deletions(-) diff --git a/client/src/uart/uart.h b/client/src/uart/uart.h index ae3896f73..f68d9c2f4 100644 --- a/client/src/uart/uart.h +++ b/client/src/uart/uart.h @@ -82,4 +82,8 @@ int uart_reconfigure_timeouts(uint32_t value); */ uint32_t uart_get_timeouts(void); +/* Specify the outbound address and port for TCP/UDP connections + */ +bool uart_bind(int sfd, char *bindAddrStr, char *bindPortStr, bool isBindingIPv6); + #endif // _UART_H_ diff --git a/client/src/uart/uart_posix.c b/client/src/uart/uart_posix.c index 1c87b2c1e..33ea9a88c 100644 --- a/client/src/uart/uart_posix.c +++ b/client/src/uart/uart_posix.c @@ -31,9 +31,11 @@ #include #include #include +#include #include #include #include +#include #ifdef HAVE_BLUEZ #include @@ -148,7 +150,7 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) { portstr = rColon + 1; } else { // two or more colon, IPv6 address - // tcp:[]: + // "tcp:[]:" // "tcp:", "tcp:[]" if (endBracket != NULL && rColon == endBracket + 1) { portstr = rColon + 1; @@ -236,6 +238,65 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) { timeout.tv_usec = UART_TCP_CLIENT_RX_TIMEOUT_MS * 1000; + // find the "bind" option + char *bindAddrPortStr = strstr(addrPortStr, ",bind="); + char *bindAddrStr = NULL; + char *bindPortStr = NULL; + bool isBindingIPv6 = false; // Assume v4 + if (bindAddrPortStr != NULL) { + *bindAddrPortStr = '\0'; // as the end of target address (and port) + bindAddrPortStr += 6; + bindAddrStr = bindAddrPortStr; + + // find the start of the bind address + char *endBracket = strrchr(bindAddrPortStr, ']'); + if (bindAddrPortStr[0] == '[') { + bindAddrStr += 1; + if (endBracket == NULL) { + PrintAndLogEx(ERR, "error: wrong address: [] unmatched in bind option"); + free(addrPortStr); + free(sp); + return INVALID_SERIAL_PORT; + } + } + + // find the bind port + char *lColon = strchr(bindAddrPortStr, ':'); + char *rColon = strrchr(bindAddrPortStr, ':'); + if (rColon == NULL) { + // no colon + // ",bind=", ",bind=[]" + bindPortStr = NULL; + } else if (lColon == rColon) { + // only one colon + // ",bind=:", ",bind=[]:" + bindPortStr = rColon + 1; + } else { + // two or more colon, IPv6 address + // ",bind=[]:" + // ",bind=", ",bind=[]" + if (endBracket != NULL && rColon == endBracket + 1) { + bindPortStr = rColon + 1; + } else { + bindPortStr = NULL; + } + isBindingIPv6 = true; + } + + // handle the end of the bind address + if (endBracket != NULL) { + *endBracket = '\0'; + } else if (rColon != NULL && lColon == rColon) { + *rColon = '\0'; + } + + // for bind option, it's possible to only specify address or port + if (strlen(bindAddrStr) == 0) + bindAddrStr = NULL; + if (bindPortStr != NULL && strlen(bindPortStr) == 0) + bindPortStr = NULL; + } + // find the start of the address char *endBracket = strrchr(addrPortStr, ']'); if (addrPortStr[0] == '[') { @@ -264,7 +325,7 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) { portstr = rColon + 1; } else { // two or more colon, IPv6 address - // tcp:[]: + // "tcp:[]:" // "tcp:", "tcp:[]" if (endBracket != NULL && rColon == endBracket + 1) { portstr = rColon + 1; @@ -304,6 +365,14 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) { if (sfd == -1) continue; + if (!uart_bind(sfd, bindAddrStr, bindPortStr, isBindingIPv6)) { + PrintAndLogEx(ERR, "error: Could not bind. errno: %d", errno); + freeaddrinfo(addr); + free(addrPortStr); + free(sp); + return INVALID_SERIAL_PORT; + } + if (connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1) break; @@ -843,4 +912,37 @@ uint32_t uart_get_speed(const serial_port sp) { }; return uiPortSpeed; } + +bool uart_bind(int sfd, char *bindAddrStr, char *bindPortStr, bool isBindingIPv6) { + if (bindAddrStr == NULL && bindPortStr == NULL) + return true; // no need to bind + + struct sockaddr_storage bindSockaddr; + memset(&bindSockaddr, 0, sizeof(bindSockaddr)); + int bindPort = 0; // 0: port unspecified + if (bindPortStr != NULL) + bindPort = atoi(bindPortStr); + + if (!isBindingIPv6) { + struct sockaddr_in *bindSockaddr4 = (struct sockaddr_in *)&bindSockaddr; + bindSockaddr4->sin_family = AF_INET; + bindSockaddr4->sin_port = htons(bindPort); + if (bindAddrStr == NULL) + bindSockaddr4->sin_addr.s_addr = INADDR_ANY; + else + bindSockaddr4->sin_addr.s_addr = inet_addr(bindAddrStr); + } else { + struct sockaddr_in6 *bindSockaddr6 = (struct sockaddr_in6 *)&bindSockaddr; + bindSockaddr6->sin6_family = AF_INET6; + bindSockaddr6->sin6_port = htons(bindPort); + if (bindAddrStr == NULL) + bindSockaddr6->sin6_addr = in6addr_any; + else + inet_pton(AF_INET6, bindAddrStr, &(bindSockaddr6->sin6_addr)); + } + + int res = bind(sfd, (struct sockaddr *)&bindSockaddr, sizeof(bindSockaddr)); + return (res >= 0); +} + #endif diff --git a/client/src/uart/uart_win32.c b/client/src/uart/uart_win32.c index a96b6e1bb..233abf640 100644 --- a/client/src/uart/uart_win32.c +++ b/client/src/uart/uart_win32.c @@ -626,4 +626,8 @@ int uart_send(const serial_port sp, const uint8_t *p_tx, const uint32_t len) { } } +bool uart_bind(int sfd, char *bindAddrStr, char *bindPortStr, bool isBindingIPv6) { + return true; +} + #endif From 15ef4f676816060d63025f4c8f160fa4a9614452 Mon Sep 17 00:00:00 2001 From: wh201906 Date: Mon, 23 Oct 2023 13:30:27 +0800 Subject: [PATCH 2/3] Add bind option (UDP, Windows) --- client/src/uart/uart.h | 2 +- client/src/uart/uart_posix.c | 15 ++--- client/src/uart/uart_win32.c | 112 ++++++++++++++++++++++++++++++++--- 3 files changed, 114 insertions(+), 15 deletions(-) diff --git a/client/src/uart/uart.h b/client/src/uart/uart.h index f68d9c2f4..954fa6685 100644 --- a/client/src/uart/uart.h +++ b/client/src/uart/uart.h @@ -84,6 +84,6 @@ uint32_t uart_get_timeouts(void); /* Specify the outbound address and port for TCP/UDP connections */ -bool uart_bind(int sfd, char *bindAddrStr, char *bindPortStr, bool isBindingIPv6); +bool uart_bind(void *socket, char *bindAddrStr, char *bindPortStr, bool isBindingIPv6); #endif // _UART_H_ diff --git a/client/src/uart/uart_posix.c b/client/src/uart/uart_posix.c index 33ea9a88c..6ed15f568 100644 --- a/client/src/uart/uart_posix.c +++ b/client/src/uart/uart_posix.c @@ -317,16 +317,16 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) { char *rColon = strrchr(addrPortStr, ':'); if (rColon == NULL) { // no colon - // "tcp:", "tcp:[]" + // "udp:", "udp:[]" portstr = "18888"; } else if (lColon == rColon) { // only one colon - // "tcp::", "tcp:[]:" + // "udp::", "udp:[]:" portstr = rColon + 1; } else { // two or more colon, IPv6 address - // "tcp:[]:" - // "tcp:", "tcp:[]" + // "udp:[]:" + // "udp:", "udp:[]" if (endBracket != NULL && rColon == endBracket + 1) { portstr = rColon + 1; } else { @@ -365,8 +365,9 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) { if (sfd == -1) continue; - if (!uart_bind(sfd, bindAddrStr, bindPortStr, isBindingIPv6)) { + if (!uart_bind(&sfd, bindAddrStr, bindPortStr, isBindingIPv6)) { PrintAndLogEx(ERR, "error: Could not bind. errno: %d", errno); + close(sfd); freeaddrinfo(addr); free(addrPortStr); free(sp); @@ -913,7 +914,7 @@ uint32_t uart_get_speed(const serial_port sp) { return uiPortSpeed; } -bool uart_bind(int sfd, char *bindAddrStr, char *bindPortStr, bool isBindingIPv6) { +bool uart_bind(void *socket, char *bindAddrStr, char *bindPortStr, bool isBindingIPv6) { if (bindAddrStr == NULL && bindPortStr == NULL) return true; // no need to bind @@ -941,7 +942,7 @@ bool uart_bind(int sfd, char *bindAddrStr, char *bindPortStr, bool isBindingIPv6 inet_pton(AF_INET6, bindAddrStr, &(bindSockaddr6->sin6_addr)); } - int res = bind(sfd, (struct sockaddr *)&bindSockaddr, sizeof(bindSockaddr)); + int res = bind(*(int *)socket, (struct sockaddr *)&bindSockaddr, sizeof(bindSockaddr)); return (res >= 0); } diff --git a/client/src/uart/uart_win32.c b/client/src/uart/uart_win32.c index 233abf640..5a5d1b415 100644 --- a/client/src/uart/uart_win32.c +++ b/client/src/uart/uart_win32.c @@ -151,7 +151,7 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) { portstr = rColon + 1; } else { // two or more colon, IPv6 address - // tcp:[]: + // "tcp:[]:" // "tcp:", "tcp:[]" if (endBracket != NULL && rColon == endBracket + 1) { portstr = rColon + 1; @@ -254,6 +254,65 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) { timeout.tv_usec = UART_TCP_CLIENT_RX_TIMEOUT_MS * 1000; + // find the "bind" option + char *bindAddrPortStr = strstr(addrPortStr, ",bind="); + char *bindAddrStr = NULL; + char *bindPortStr = NULL; + bool isBindingIPv6 = false; // Assume v4 + if (bindAddrPortStr != NULL) { + *bindAddrPortStr = '\0'; // as the end of target address (and port) + bindAddrPortStr += 6; + bindAddrStr = bindAddrPortStr; + + // find the start of the bind address + char *endBracket = strrchr(bindAddrPortStr, ']'); + if (bindAddrPortStr[0] == '[') { + bindAddrStr += 1; + if (endBracket == NULL) { + PrintAndLogEx(ERR, "error: wrong address: [] unmatched in bind option"); + free(addrPortStr); + free(sp); + return INVALID_SERIAL_PORT; + } + } + + // find the bind port + char *lColon = strchr(bindAddrPortStr, ':'); + char *rColon = strrchr(bindAddrPortStr, ':'); + if (rColon == NULL) { + // no colon + // ",bind=", ",bind=[]" + bindPortStr = NULL; + } else if (lColon == rColon) { + // only one colon + // ",bind=:", ",bind=[]:" + bindPortStr = rColon + 1; + } else { + // two or more colon, IPv6 address + // ",bind=[]:" + // ",bind=", ",bind=[]" + if (endBracket != NULL && rColon == endBracket + 1) { + bindPortStr = rColon + 1; + } else { + bindPortStr = NULL; + } + isBindingIPv6 = true; + } + + // handle the end of the bind address + if (endBracket != NULL) { + *endBracket = '\0'; + } else if (rColon != NULL && lColon == rColon) { + *rColon = '\0'; + } + + // for bind option, it's possible to only specify address or port + if (strlen(bindAddrStr) == 0) + bindAddrStr = NULL; + if (bindPortStr != NULL && strlen(bindPortStr) == 0) + bindPortStr = NULL; + } + // find the start of the address char *endBracket = strrchr(addrPortStr, ']'); if (addrPortStr[0] == '[') { @@ -274,16 +333,16 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) { char *rColon = strrchr(addrPortStr, ':'); if (rColon == NULL) { // no colon - // "tcp:", "tcp:[]" + // "udp:", "udp:[]" portstr = "18888"; } else if (lColon == rColon) { // only one colon - // "tcp::", "tcp:[]:" + // "udp::", "udp:[]:" portstr = rColon + 1; } else { // two or more colon, IPv6 address - // tcp:[]: - // "tcp:", "tcp:[]" + // "udp:[]:" + // "udp:", "udp:[]" if (endBracket != NULL && rColon == endBracket + 1) { portstr = rColon + 1; } else { @@ -333,6 +392,17 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) { if (hSocket == INVALID_SOCKET) continue; + if (!uart_bind(&hSocket, bindAddrStr, bindPortStr, isBindingIPv6)) { + PrintAndLogEx(ERR, "error: Could not bind. error: %u", WSAGetLastError()); + closesocket(hSocket); + hSocket = INVALID_SOCKET; + freeaddrinfo(addr); + free(addrPortStr); + free(sp); + WSACleanup(); + return INVALID_SERIAL_PORT; + } + if (connect(hSocket, rp->ai_addr, (int)rp->ai_addrlen) != INVALID_SOCKET) break; @@ -626,8 +696,36 @@ int uart_send(const serial_port sp, const uint8_t *p_tx, const uint32_t len) { } } -bool uart_bind(int sfd, char *bindAddrStr, char *bindPortStr, bool isBindingIPv6) { - return true; +bool uart_bind(void *socket, char *bindAddrStr, char *bindPortStr, bool isBindingIPv6) { + if (bindAddrStr == NULL && bindPortStr == NULL) + return true; // no need to bind + + struct sockaddr_storage bindSockaddr; + memset(&bindSockaddr, 0, sizeof(bindSockaddr)); + int bindPort = 0; // 0: port unspecified + if (bindPortStr != NULL) + bindPort = atoi(bindPortStr); + + if (!isBindingIPv6) { + struct sockaddr_in *bindSockaddr4 = (struct sockaddr_in *)&bindSockaddr; + bindSockaddr4->sin_family = AF_INET; + bindSockaddr4->sin_port = htons(bindPort); + if (bindAddrStr == NULL) + bindSockaddr4->sin_addr.s_addr = INADDR_ANY; + else + bindSockaddr4->sin_addr.s_addr = inet_addr(bindAddrStr); + } else { + struct sockaddr_in6 *bindSockaddr6 = (struct sockaddr_in6 *)&bindSockaddr; + bindSockaddr6->sin6_family = AF_INET6; + bindSockaddr6->sin6_port = htons(bindPort); + if (bindAddrStr == NULL) + bindSockaddr6->sin6_addr = in6addr_any; + else + inet_pton(AF_INET6, bindAddrStr, &(bindSockaddr6->sin6_addr)); + } + + int res = bind(*(SOCKET *)socket, (struct sockaddr *)&bindSockaddr, sizeof(bindSockaddr)); + return (res >= 0); } #endif From 56b4bda50d1cd708616bd9a583db6baa286215f0 Mon Sep 17 00:00:00 2001 From: wh201906 Date: Mon, 23 Oct 2023 13:45:06 +0800 Subject: [PATCH 3/3] Add bind option for TCP --- CHANGELOG.md | 1 + client/src/uart/uart_posix.c | 68 +++++++++++++++++++++++++++++++++++ client/src/uart/uart_win32.c | 70 ++++++++++++++++++++++++++++++++++++ 3 files changed, 139 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 084441cc8..24ea5b3a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] + - Added `bind` option for network connections to specify the outbound address and port (@wh201906) - Added new command `data bmap` - breaks down a hexvalue to a binary template (@iceman1001) - Changed aid_desfire.json - added entreis from the Metrodroid project (@iceman1001) - Changed mad.json - added entries from the Metrodroid project (@iceman1001) diff --git a/client/src/uart/uart_posix.c b/client/src/uart/uart_posix.c index 6ed15f568..ecb488633 100644 --- a/client/src/uart/uart_posix.c +++ b/client/src/uart/uart_posix.c @@ -121,6 +121,65 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) { timeout.tv_usec = UART_TCP_CLIENT_RX_TIMEOUT_MS * 1000; + // find the "bind" option + char *bindAddrPortStr = strstr(addrPortStr, ",bind="); + char *bindAddrStr = NULL; + char *bindPortStr = NULL; + bool isBindingIPv6 = false; // Assume v4 + if (bindAddrPortStr != NULL) { + *bindAddrPortStr = '\0'; // as the end of target address (and port) + bindAddrPortStr += 6; + bindAddrStr = bindAddrPortStr; + + // find the start of the bind address + char *endBracket = strrchr(bindAddrPortStr, ']'); + if (bindAddrPortStr[0] == '[') { + bindAddrStr += 1; + if (endBracket == NULL) { + PrintAndLogEx(ERR, "error: wrong address: [] unmatched in bind option"); + free(addrPortStr); + free(sp); + return INVALID_SERIAL_PORT; + } + } + + // find the bind port + char *lColon = strchr(bindAddrPortStr, ':'); + char *rColon = strrchr(bindAddrPortStr, ':'); + if (rColon == NULL) { + // no colon + // ",bind=", ",bind=[]" + bindPortStr = NULL; + } else if (lColon == rColon) { + // only one colon + // ",bind=:", ",bind=[]:" + bindPortStr = rColon + 1; + } else { + // two or more colon, IPv6 address + // ",bind=[]:" + // ",bind=", ",bind=[]" + if (endBracket != NULL && rColon == endBracket + 1) { + bindPortStr = rColon + 1; + } else { + bindPortStr = NULL; + } + isBindingIPv6 = true; + } + + // handle the end of the bind address + if (endBracket != NULL) { + *endBracket = '\0'; + } else if (rColon != NULL && lColon == rColon) { + *rColon = '\0'; + } + + // for bind option, it's possible to only specify address or port + if (strlen(bindAddrStr) == 0) + bindAddrStr = NULL; + if (bindPortStr != NULL && strlen(bindPortStr) == 0) + bindPortStr = NULL; + } + // find the start of the address char *endBracket = strrchr(addrPortStr, ']'); if (addrPortStr[0] == '[') { @@ -190,6 +249,15 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) { if (sfd == -1) continue; + if (!uart_bind(&sfd, bindAddrStr, bindPortStr, isBindingIPv6)) { + PrintAndLogEx(ERR, "error: Could not bind. errno: %d", errno); + close(sfd); + freeaddrinfo(addr); + free(addrPortStr); + free(sp); + return INVALID_SERIAL_PORT; + } + if (connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1) break; diff --git a/client/src/uart/uart_win32.c b/client/src/uart/uart_win32.c index 5a5d1b415..72faf97b4 100644 --- a/client/src/uart/uart_win32.c +++ b/client/src/uart/uart_win32.c @@ -123,6 +123,65 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) { timeout.tv_usec = UART_TCP_CLIENT_RX_TIMEOUT_MS * 1000; + // find the "bind" option + char *bindAddrPortStr = strstr(addrPortStr, ",bind="); + char *bindAddrStr = NULL; + char *bindPortStr = NULL; + bool isBindingIPv6 = false; // Assume v4 + if (bindAddrPortStr != NULL) { + *bindAddrPortStr = '\0'; // as the end of target address (and port) + bindAddrPortStr += 6; + bindAddrStr = bindAddrPortStr; + + // find the start of the bind address + char *endBracket = strrchr(bindAddrPortStr, ']'); + if (bindAddrPortStr[0] == '[') { + bindAddrStr += 1; + if (endBracket == NULL) { + PrintAndLogEx(ERR, "error: wrong address: [] unmatched in bind option"); + free(addrPortStr); + free(sp); + return INVALID_SERIAL_PORT; + } + } + + // find the bind port + char *lColon = strchr(bindAddrPortStr, ':'); + char *rColon = strrchr(bindAddrPortStr, ':'); + if (rColon == NULL) { + // no colon + // ",bind=", ",bind=[]" + bindPortStr = NULL; + } else if (lColon == rColon) { + // only one colon + // ",bind=:", ",bind=[]:" + bindPortStr = rColon + 1; + } else { + // two or more colon, IPv6 address + // ",bind=[]:" + // ",bind=", ",bind=[]" + if (endBracket != NULL && rColon == endBracket + 1) { + bindPortStr = rColon + 1; + } else { + bindPortStr = NULL; + } + isBindingIPv6 = true; + } + + // handle the end of the bind address + if (endBracket != NULL) { + *endBracket = '\0'; + } else if (rColon != NULL && lColon == rColon) { + *rColon = '\0'; + } + + // for bind option, it's possible to only specify address or port + if (strlen(bindAddrStr) == 0) + bindAddrStr = NULL; + if (bindPortStr != NULL && strlen(bindPortStr) == 0) + bindPortStr = NULL; + } + // find the start of the address char *endBracket = strrchr(addrPortStr, ']'); if (addrPortStr[0] == '[') { @@ -202,6 +261,17 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) { if (hSocket == INVALID_SOCKET) continue; + if (!uart_bind(&hSocket, bindAddrStr, bindPortStr, isBindingIPv6)) { + PrintAndLogEx(ERR, "error: Could not bind. error: %u", WSAGetLastError()); + closesocket(hSocket); + hSocket = INVALID_SOCKET; + freeaddrinfo(addr); + free(addrPortStr); + free(sp); + WSACleanup(); + return INVALID_SERIAL_PORT; + } + if (connect(hSocket, rp->ai_addr, (int)rp->ai_addrlen) != INVALID_SOCKET) break;