From 56b4bda50d1cd708616bd9a583db6baa286215f0 Mon Sep 17 00:00:00 2001 From: wh201906 Date: Mon, 23 Oct 2023 13:45:06 +0800 Subject: [PATCH] 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;