diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b5156d96..b5ca6b063 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) - Changed `lf em 4x05 dump` - now supports the `--ns` nosave parameter (@iceman1001) - 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) diff --git a/client/src/uart/uart.h b/client/src/uart/uart.h index ae3896f73..954fa6685 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(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 1c87b2c1e..ecb488633 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 @@ -119,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] == '[') { @@ -148,7 +209,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; @@ -188,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; @@ -236,6 +306,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] == '[') { @@ -256,16 +385,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 { @@ -304,6 +433,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; @@ -843,4 +981,37 @@ uint32_t uart_get_speed(const serial_port sp) { }; return uiPortSpeed; } + +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(*(int *)socket, (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..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] == '[') { @@ -151,7 +210,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; @@ -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; @@ -254,6 +324,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 +403,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 +462,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,4 +766,36 @@ int uart_send(const serial_port sp, const uint8_t *p_tx, const uint32_t len) { } } +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