fix: pasv port chosen after availability checks

This commit is contained in:
Ugo
2025-05-26 20:24:13 +01:00
parent 5b6919a7b1
commit cdc4e4adc9
3 changed files with 183 additions and 75 deletions

View File

@ -123,6 +123,8 @@ void *connectionWorkerHandle(cleanUpWorkerArgs *args)
// Enable cancellation for this thread // Enable cancellation for this thread
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
pthread_cleanup_push(workerCleanup, args); pthread_cleanup_push(workerCleanup, args);
ftpData->clients[theSocketId].workerData.threadIsAlive = 1; ftpData->clients[theSocketId].workerData.threadIsAlive = 1;
ftpData->clients[theSocketId].workerData.threadHasBeenCreated = 1; ftpData->clients[theSocketId].workerData.threadHasBeenCreated = 1;

View File

@ -257,28 +257,45 @@ void appendToDynamicStringDataType(dynamicStringDataType *dynamicString, char *t
void setRandomicPort(ftpDataType *data, int socketPosition) void setRandomicPort(ftpDataType *data, int socketPosition)
{ {
unsigned short int randomicPort = 5000; unsigned short int randomicPort;
int i = 0; int maxAttempts = data->ftpParameters.connectionPortMax - data->ftpParameters.connectionPortMin +1;
int attempt = 0;
int conflict;
randomicPort = data->ftpParameters.connectionPortMin + (rand()%(data->ftpParameters.connectionPortMax - data->ftpParameters.connectionPortMin)); while (attempt++ < maxAttempts)
{
// Generate a random port in range
randomicPort = data->ftpParameters.connectionPortMin +
(rand() % (data->ftpParameters.connectionPortMax - data->ftpParameters.connectionPortMin + 1));
while (i < data->ftpParameters.maxClients) // Check against other clients
{ conflict = 0;
if (randomicPort == data->clients[i].workerData.connectionPort) for (int i = 0; i < data->ftpParameters.maxClients; ++i)
{ {
randomicPort = data->ftpParameters.connectionPortMin + (rand()%(data->ftpParameters.connectionPortMax - data->ftpParameters.connectionPortMin)); if (i != socketPosition &&
i = 0; data->clients[i].workerData.connectionPort == randomicPort)
} {
else conflict = 1;
{ break;
i++; }
} }
}
data->clients[socketPosition].workerData.connectionPort = randomicPort; if (conflict)
continue;
my_printf("\n data->clients[%d].workerData.connectionPort = %d", socketPosition, data->clients[socketPosition].workerData.connectionPort); // Check if port is in use on the system
if (!isPortInUse(randomicPort))
{
data->clients[socketPosition].workerData.connectionPort = randomicPort;
my_printf("\n data->clients[%d].workerData.connectionPort = %d",
socketPosition, randomicPort);
return;
}
}
// If were here, we failed to find a port
addLog("Failed to find available random port", CURRENT_FILE, CURRENT_LINE, CURRENT_FUNC);
data->clients[socketPosition].workerData.connectionPort = 0;
} }
int writeListDataInfoToSocket(ftpDataType *ftpData, int clientId, int *filesNumber, int commandType, DYNMEM_MemoryTable_DataType **memoryTable) int writeListDataInfoToSocket(ftpDataType *ftpData, int clientId, int *filesNumber, int commandType, DYNMEM_MemoryTable_DataType **memoryTable)
@ -675,16 +692,24 @@ void deleteListDataInfoVector(DYNV_VectorGenericDataType *theVector)
void cancelWorker(ftpDataType *data, int clientId) void cancelWorker(ftpDataType *data, int clientId)
{ {
void *pReturn; void *pReturn;
addLog("Cancelling thread because is busy", CURRENT_FILE, CURRENT_LINE, CURRENT_FUNC);
int returnCode = pthread_cancel(data->clients[clientId].workerData.workerThread); if (data->clients[clientId].workerData.threadHasBeenCreated) {
if (returnCode != 0) addLog("Cancelling thread because it is busy", CURRENT_FILE, CURRENT_LINE, CURRENT_FUNC);
{
addLog("Cancelling thread ERROR", CURRENT_FILE, CURRENT_LINE, CURRENT_FUNC); int returnCode = pthread_cancel(data->clients[clientId].workerData.workerThread);
if (returnCode != 0) {
addLog("Cancelling thread ERROR", CURRENT_FILE, CURRENT_LINE, CURRENT_FUNC);
}
returnCode = pthread_join(data->clients[clientId].workerData.workerThread, &pReturn);
if (returnCode != 0) {
addLog("Joining thread ERROR", CURRENT_FILE, CURRENT_LINE, CURRENT_FUNC);
}
data->clients[clientId].workerData.threadHasBeenCreated = 0;
data->clients[clientId].workerData.workerThread = 0; // Reset thread ID
} }
returnCode = pthread_join(data->clients[clientId].workerData.workerThread, &pReturn);
data->clients[clientId].workerData.threadHasBeenCreated = 0;
} }
void resetWorkerData(ftpDataType *data, int clientId, int isInitialization) void resetWorkerData(ftpDataType *data, int clientId, int isInitialization)

View File

@ -360,6 +360,38 @@ int getMaximumSocketFd(int mainSocket, ftpDataType * ftpData)
#ifdef IPV6_ENABLED #ifdef IPV6_ENABLED
int isPortInUse(int port) {
int sockfd;
struct sockaddr_in6 addr;
sockfd = socket(AF_INET6, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("socket");
return 1; // Assume port is in use if we can't create a socket
}
int reuse = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
memset(&addr, 0, sizeof(addr));
addr.sin6_family = AF_INET6;
addr.sin6_port = htons(port);
addr.sin6_addr = in6addr_any;
int result = bind(sockfd, (struct sockaddr*)&addr, sizeof(addr));
close(sockfd); // Always close after test
if (result == 0) {
return 0; // Port is free
} else if (errno == EADDRINUSE) {
return 1; // Port is in use
} else {
perror("bind");
return 1; // Other error, treat as in-use
}
}
int createSocket(ftpDataType * ftpData) int createSocket(ftpDataType * ftpData)
{ {
//my_printf("\nCreating main socket on port %d", ftpData->ftpParameters.port); //my_printf("\nCreating main socket on port %d", ftpData->ftpParameters.port);
@ -515,6 +547,37 @@ int createPassiveSocket(int port)
#else #else
int isPortInUse(int port) {
int sockfd;
struct sockaddr_in addr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("socket");
return 1; // Assume port is in use if we can't create a socket
}
int reuse = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY;
int result = bind(sockfd, (struct sockaddr*)&addr, sizeof(addr));
close(sockfd); // Always close after test
if (result == 0) {
return 0; // Port is free
} else if (errno == EADDRINUSE) {
return 1; // Port is in use
} else {
perror("bind");
return 1; // Other error, treat as in-use
}
}
int createSocket(ftpDataType * ftpData) int createSocket(ftpDataType * ftpData)
{ {
//my_printf("\nCreating main socket on port %d", ftpData->ftpParameters.port); //my_printf("\nCreating main socket on port %d", ftpData->ftpParameters.port);
@ -581,68 +644,86 @@ int createSocket(ftpDataType * ftpData)
int createPassiveSocket(int port) int createPassiveSocket(int port)
{ {
int sock, returnCode; int sock, returnCode;
struct sockaddr_in temp; struct sockaddr_in serveraddr;
int max_retries = 12; // number of bind retries
int retry_delay_sec = 1; // delay between retries
//Socket creation if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
sock = socket(AF_INET, SOCK_STREAM, 0); {
if (sock == -1) perror("socket() failed");
{ addLog("socket failed", CURRENT_FILE, CURRENT_LINE, CURRENT_FUNC);
return -1; return -1;
} }
temp.sin_family = AF_INET; memset(&serveraddr, 0, sizeof(serveraddr));
temp.sin_addr.s_addr = INADDR_ANY; serveraddr.sin_family = AF_INET;
temp.sin_port = htons(port); serveraddr.sin_port = htons(port);
serveraddr.sin_addr.s_addr = INADDR_ANY;
int reuse = 1;
int reuse = 1;
#ifdef SO_REUSEADDR #ifdef SO_REUSEADDR
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse, sizeof(reuse)) < 0) if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse, sizeof(reuse)) < 0)
{ {
perror("setsockopt(SO_REUSEADDR) failed"); perror("setsockopt(SO_REUSEADDR) failed");
my_printfError("setsockopt(SO_REUSEADDR) failed"); my_printfError("setsockopt(SO_REUSEADDR) failed");
addLog("setsocketerror", CURRENT_FILE, CURRENT_LINE, CURRENT_FUNC); addLog("setsocketerror", CURRENT_FILE, CURRENT_LINE, CURRENT_FUNC);
} }
#endif #endif
#ifdef SO_REUSEPORT #ifdef SO_REUSEPORT
if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (const char*)&reuse, sizeof(reuse)) < 0) if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (const char*)&reuse, sizeof(reuse)) < 0)
{ {
perror("setsockopt(SO_REUSEADDR) failed"); perror("setsockopt(SO_REUSEPORT) failed");
my_printfError("setsockopt(SO_REUSEADDR) failed"); my_printfError("setsockopt(SO_REUSEPORT) failed");
addLog("set socket error", CURRENT_FILE, CURRENT_LINE, CURRENT_FUNC); addLog("setsocketerror", CURRENT_FILE, CURRENT_LINE, CURRENT_FUNC);
} }
#endif #endif
//Bind socket // Retry bind if it fails with EADDRINUSE
returnCode = bind(sock,(struct sockaddr*) &temp,sizeof(temp)); for (int i = 0; i < max_retries; i++)
{
returnCode = bind(sock, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
if (returnCode == 0)
{
if (i > 0)
printf("\n Success After: %d attempts", i);
break;
}
if (returnCode == -1) if (errno == EADDRINUSE)
{ {
my_printf("\n Could not bind %d errno = %d", sock, errno); my_printf("Bind failed with EADDRINUSE on port: %d, retrying %d/%d...\n", port, i + 1, max_retries);
sleep(retry_delay_sec);
}
else
{
perror("bind() failed");
addLog("bind failed", CURRENT_FILE, CURRENT_LINE, CURRENT_FUNC);
close(sock);
return -1;
}
}
if (sock != -1) if (returnCode != 0)
{ {
close(sock); my_printf("Bind failed after %d retries, errno=%d\n", max_retries, errno);
} addLog("bind failed after all attempts!", CURRENT_FILE, CURRENT_LINE, CURRENT_FUNC);
return returnCode; close(sock);
} return -1;
}
//Number of client allowed // Start listening
returnCode = listen(sock, 1); returnCode = listen(sock, 1);
if (returnCode == -1)
{
addLog("listen failed", CURRENT_FILE, CURRENT_LINE, CURRENT_FUNC);
my_printf("\nCould not listen %d errno = %d", sock, errno);
close(sock);
return -1;
}
if (returnCode == -1) return sock;
{
my_printf("\n Could not listen %d errno = %d", sock, errno);
if (sock != -1)
{
close(sock);
}
return returnCode;
}
return sock;
} }
#endif #endif