Related
i am working on an project and i need to edit a tcp packet data. I use Windivert for this, I can find my packet and edit also i dont vhange packet length just replace some walues with random values(i try this with socket rediretion and edit it will work but i want to use windivert) but when re-inject this server responde with FIN,ACK Flags. Here is my code, I can' figure out problem if anyone can help me i will be happy.
#include <winsock2.h>
#include <windows.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "windivert.h"
#define MAXBUF WINDIVERT_MTU_MAX
#define PROXY_PORT 34010
#define ALT_PORT 43010
#define MAX_LINE 65
/*
* Proxy server configuration.
*/
typedef struct
{
UINT16 proxy_port;
UINT16 alt_port;
} PROXY_CONFIG, *PPROXY_CONFIG;
typedef struct
{
SOCKET s;
UINT16 alt_port;
struct in_addr dest;
} PROXY_CONNECTION_CONFIG, *PPROXY_CONNECTION_CONFIG;
typedef struct
{
BOOL inbound;
SOCKET s;
SOCKET t;
} PROXY_TRANSFER_CONFIG, *PPROXY_TRANSFER_CONFIG;
/*
* Lock to sync output.
*/
static HANDLE lock;
/*
* Prototypes.
*/
static DWORD proxy(LPVOID arg);
static DWORD proxy_connection_handler(LPVOID arg);
static DWORD proxy_transfer_handler(LPVOID arg);
/*
* Error handling.
*/
static void message(const char *msg, ...)
{
va_list args;
va_start(args, msg);
WaitForSingleObject(lock, INFINITE);
vfprintf(stderr, msg, args);
putc('\n', stderr);
ReleaseMutex(lock);
va_end(args);
}
#define error(msg, ...) \
do { \
message("error: " msg, ## __VA_ARGS__); \
exit(EXIT_FAILURE); \
} while (FALSE)
#define warning(msg, ...) \
message("warning: " msg, ## __VA_ARGS__)
int generate_random(int l, int r) { //this will generate random number in range l and r
int rand_num = (rand() % (r - l + 1)) + l;
return rand_num;
}
/*
* Entry.
*/
int __cdecl main(int argc, char **argv)
{
HANDLE handle, thread;
UINT16 port, proxy_port, alt_port;
int r;
char filter[256];
INT16 priority = 123; // Arbitrary.
PPROXY_CONFIG config;
unsigned char packet[MAXBUF];
UINT packet_len;
WINDIVERT_ADDRESS addr;
PWINDIVERT_IPHDR ip_header;
PWINDIVERT_TCPHDR tcp_header;
DWORD len;
// Init.
if (argc != 2)
{
fprintf(stderr, "usage: %s dest-port\n", argv[0]);
exit(EXIT_FAILURE);
}
port = (UINT16)atoi(argv[1]);
if (port < 0 || port > 0xFFFF)
{
fprintf(stderr, "error: invalid port number (%d)\n", port);
exit(EXIT_FAILURE);
}
proxy_port = (port == PROXY_PORT? PROXY_PORT+1: PROXY_PORT);
alt_port = (port == ALT_PORT? ALT_PORT+1: ALT_PORT);
lock = CreateMutex(NULL, FALSE, NULL);
if (lock == NULL)
{
fprintf(stderr, "error: failed to create mutex (%d)\n",
GetLastError());
exit(EXIT_FAILURE);
}
// Divert all traffic to/from `port', `proxy_port' and `alt_port'.
r = snprintf(filter, sizeof(filter),
"tcp and "
"(tcp.DstPort == %d or tcp.DstPort == %d or tcp.DstPort == %d or "
"tcp.SrcPort == %d or tcp.SrcPort == %d or tcp.SrcPort == %d)",
port, proxy_port, alt_port, port, proxy_port, alt_port);
if (r < 0 || r >= sizeof(filter))
{
error("failed to create filter string");
}
handle = WinDivertOpen(filter, WINDIVERT_LAYER_NETWORK, priority, 0);
if (handle == INVALID_HANDLE_VALUE)
{
error("failed to open the WinDivert device (%d)", GetLastError());
}
// Spawn proxy thread,
config = (PPROXY_CONFIG)malloc(sizeof(PROXY_CONFIG));
if (config == NULL)
{
error("failed to allocate memory");
}
config->proxy_port = proxy_port;
config->alt_port = alt_port;
thread = CreateThread(NULL, 1, (LPTHREAD_START_ROUTINE)proxy,
(LPVOID)config, 0, NULL);
if (thread == NULL)
{
error("failed to create thread (%d)", GetLastError());
}
CloseHandle(thread);
// Main loop:
while (TRUE)
{
if (!WinDivertRecv(handle, packet, sizeof(packet), &packet_len, &addr))
{
warning("failed to read packet (%d)", GetLastError());
continue;
}
WinDivertHelperParsePacket(packet, packet_len, &ip_header, NULL, NULL,
NULL, NULL, &tcp_header, NULL, NULL, NULL, NULL, NULL);
if (ip_header == NULL || tcp_header == NULL)
{
warning("failed to parse packet (%d)", GetLastError());
continue;
}
if (addr.Outbound)
{
if (tcp_header->DstPort == htons(port))
{
// Reflect: PORT ---> PROXY
UINT32 dst_addr = ip_header->DstAddr;
tcp_header->DstPort = htons(proxy_port);
ip_header->DstAddr = ip_header->SrcAddr;
ip_header->SrcAddr = dst_addr;
addr.Outbound = FALSE;
if ((UINT8)packet[40] == 128 && (UINT8)packet[41] == 0 && (UINT8)packet[42] == 119 && (UINT8)packet[43] == 162 && (UINT8)packet[172] == 185)
{
srand(time(0));
printf(" \n founded \n");
(UINT8)packet[168] = generate_random(1, 155);
(UINT8)packet[172] = generate_random(1, 155);
WinDivertHelperCalcChecksums(packet, packet_len, &addr, 0);
if (!WinDivertSend(handle, packet, packet_len, NULL, &addr))
{
// Handle send error
continue;
}
}
}
else if (tcp_header->SrcPort == htons(proxy_port))
{
// Reflect: PROXY ---> PORT
UINT32 dst_addr = ip_header->DstAddr;
tcp_header->SrcPort = htons(port);
ip_header->DstAddr = ip_header->SrcAddr;
ip_header->SrcAddr = dst_addr;
addr.Outbound = FALSE;
}
else if (tcp_header->DstPort == htons(alt_port))
{
// Redirect: ALT ---> PORT
tcp_header->DstPort = htons(port);
}
}
else
{
if (tcp_header->SrcPort == htons(port))
{
// Redirect: PORT ---> ALT
tcp_header->SrcPort = htons(alt_port);
}
}
WinDivertHelperCalcChecksums(packet, packet_len, &addr, 0);
if (!WinDivertSend(handle, packet, packet_len, NULL, &addr))
{
warning("failed to send packet (%d)", GetLastError());
continue;
}
}
return 0;
}
/*
* Proxy server thread.
*/
static DWORD proxy(LPVOID arg)
{
PPROXY_CONFIG config = (PPROXY_CONFIG)arg;
UINT16 proxy_port = config->proxy_port;
UINT16 alt_port = config->alt_port;
int on = 1;
WSADATA wsa_data;
WORD wsa_version = MAKEWORD(2, 2);
struct sockaddr_in addr;
SOCKET s;
HANDLE thread;
free(config);
if (WSAStartup(wsa_version, &wsa_data) != 0)
{
error("failed to start WSA (%d)", GetLastError());
}
s = socket(AF_INET, SOCK_STREAM, 0);
if (s == INVALID_SOCKET)
{
error("failed to create socket (%d)", WSAGetLastError());
}
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(int))
== SOCKET_ERROR)
{
error("failed to re-use address (%d)", GetLastError());
}
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(proxy_port);
if (bind(s, (SOCKADDR *)&addr, sizeof(addr)) == SOCKET_ERROR)
{
error("failed to bind socket (%d)", WSAGetLastError());
}
if (listen(s, 16) == SOCKET_ERROR)
{
error("failed to listen socket (%d)", WSAGetLastError());
}
while (TRUE)
{
// Wait for a new connection.
PPROXY_CONNECTION_CONFIG config;
int size = sizeof(addr);
SOCKET t = accept(s, (SOCKADDR *)&addr, &size);
if (t == INVALID_SOCKET)
{
warning("failed to accept socket (%d)", WSAGetLastError());
continue;
}
// Spawn proxy connection handler thread.
config = (PPROXY_CONNECTION_CONFIG)
malloc(sizeof(PROXY_CONNECTION_CONFIG));
if (config == NULL)
{
error("failed to allocate memory");
}
config->s = t;
config->alt_port = alt_port;
config->dest = addr.sin_addr;
thread = CreateThread(NULL, 1,
(LPTHREAD_START_ROUTINE)proxy_connection_handler,
(LPVOID)config, 0, NULL);
if (thread == NULL)
{
warning("failed to create thread (%d)", GetLastError());
closesocket(t);
free(config);
continue;
}
CloseHandle(thread);
}
}
/*
* Proxy connection handler thread.
*/
static DWORD proxy_connection_handler(LPVOID arg)
{
PPROXY_TRANSFER_CONFIG config1, config2;
HANDLE thread;
PPROXY_CONNECTION_CONFIG config = (PPROXY_CONNECTION_CONFIG)arg;
SOCKET s = config->s, t;
UINT16 alt_port = config->alt_port;
struct in_addr dest = config->dest;
struct sockaddr_in addr;
free(config);
t = socket(AF_INET, SOCK_STREAM, 0);
if (t == INVALID_SOCKET)
{
warning("failed to create socket (%d)", WSAGetLastError());
closesocket(s);
return 0;
}
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(alt_port);
addr.sin_addr = dest;
if (connect(t, (SOCKADDR *)&addr, sizeof(addr)) == SOCKET_ERROR)
{
warning("failed to connect socket (%d)", WSAGetLastError());
closesocket(s);
closesocket(t);
return 0;
}
config1 = (PPROXY_TRANSFER_CONFIG)malloc(sizeof(PROXY_TRANSFER_CONFIG));
config2 = (PPROXY_TRANSFER_CONFIG)malloc(sizeof(PROXY_TRANSFER_CONFIG));
if (config1 == NULL || config2 == NULL)
{
error("failed to allocate memory");
}
config1->inbound = FALSE;
config2->inbound = TRUE;
config2->t = config1->s = s;
config2->s = config1->t = t;
thread = CreateThread(NULL, 1,
(LPTHREAD_START_ROUTINE)proxy_transfer_handler, (LPVOID)config1, 0,
NULL);
if (thread == NULL)
{
warning("failed to create thread (%d)", GetLastError());
closesocket(s);
closesocket(t);
free(config1);
free(config2);
return 0;
}
proxy_transfer_handler((LPVOID)config2);
WaitForSingleObject(thread, INFINITE);
CloseHandle(thread);
closesocket(s);
closesocket(t);
return 0;
}
/*
* Handle the transfer of data from one socket to another.
*/
static DWORD proxy_transfer_handler(LPVOID arg)
{
PPROXY_TRANSFER_CONFIG config = (PPROXY_TRANSFER_CONFIG)arg;
BOOL inbound = config->inbound;
SOCKET s = config->s, t = config->t;
char buf[8192];
int len, len2, i;
HANDLE console;
free(config);
while (TRUE)
{
1
len = recv(s, buf, sizeof(buf), 0);
if (len == SOCKET_ERROR)
{
warning("failed to recv from socket (%d)", WSAGetLastError());
shutdown(s, SD_BOTH);
shutdown(t, SD_BOTH);
return 0;
}
if (len == 0)
{
shutdown(s, SD_RECEIVE);
shutdown(t, SD_SEND);
return 0;
}
// Dump stream information to the screen.
console = GetStdHandle(STD_OUTPUT_HANDLE);
WaitForSingleObject(lock, INFINITE);
printf("[%.4d] ", len);
SetConsoleTextAttribute(console,
(inbound? FOREGROUND_RED: FOREGROUND_GREEN));
for (i = 0; i < len && i < MAX_LINE; i++)
{
putchar((isprint(buf[i])? buf[i]: '.'));
}
SetConsoleTextAttribute(console,
FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
printf("%s\n", (len > MAX_LINE? "...": ""));
ReleaseMutex(lock);
// Send data to t.
int i;
for (i = 0; i < len; )
{
len2 = send(t, buf+i, len-i, 0);
if (len2 == SOCKET_ERROR)
{
warning("failed to send to socket (%d)", WSAGetLastError());
shutdown(s, SD_BOTH);
shutdown(t, SD_BOTH);
return 0;
}
i += len2;
}
}
return 0;
}
wireshark outputs are here ;
When i don't edit packet here is wireshark output;
Wireshark Output without edit
When I try to edit packet it is edited but wireshark output is like that (editted packets length is 188);
Wireshark Output With Edit
I wrote a simple protocol in which Im able to exchange files / text messages between client and server. If the client send a text to server, server should simply echo it back. On the other hand, when client send a special command (for example, SEND_TXT_FILE) server should receive a file uploaded by client to the server.
I almost got it work. However, there's still problem with sending files. Sever does not save the whole file, it only creates it and disconnects.
Here's the protocol:
CLIENT ---------- text1 ----------> SERVER
CLIENT <---------- text1 ---------- SERVER
CLIENT ---------- text2 ----------> SERVER
CLIENT <---------- text3 ---------- SERVER
CLIENT ---------- SENDTXTFILE ----------> SERVER
CLIENT <---------- OK ---------- SERVER
CLIENT ---------- FILENAME ----------> SERVER
CLIENT <---------- OK ---------- SERVER
CLIENT ---------- file content ----------> SERVER
CLIENT <--------- FILE_UPLOADED --------- SERVER
CLIENT ---------- text3 ----------> SERVER
CLIENT <---------- text3 ---------- SERVER
How can I solve this?
server.c
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>
#include <string.h>
#define BUFFER_SIZE 1024
int send_all(int sockfd, const char *buf, int len)
{
ssize_t n;
while (len > 0)
{
n = send(sockfd, buf, len, 0);
if (n < 0)
return -1;
buf += n;
len -= n;
}
return 0;
}
int recv_all(int sockfd, char *buf, int len)
{
ssize_t n;
while (len > 0)
{
n = recv(sockfd, buf, len, 0);
if (n <= 0)
return n;
buf += n;
len -= n;
}
return 1;
}
int recv_txt_file(int sockfd, int len, const char *filename)
{
FILE *fp = fopen(filename, "wb");
int total = 0, b = 0;
char buffer[BUFFER_SIZE];
memset(buffer, '\0', BUFFER_SIZE);
if (fp != NULL)
{
while (recv_all(sockfd, buffer, len) != 1)
{
total += b;
fwrite(buffer, 1, b, fp);
}
printf("Received byte: %d\n",total);
if (b < 0)
perror("Receiving");
fclose(fp);
}
else
{
perror("File");
}
close(sockfd);
}
int main()
{
int port = 6666;
int server_fd, client_fd, read;
struct sockaddr_in server, client;
char buffer[BUFFER_SIZE], filename[BUFFER_SIZE];
char remote_ip[16];
int remote_port, res = 0;
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd < 0)
{
perror("Could not create socket");
return 1;
}
int optval = 1;
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&optval, sizeof(int));
memset(&server, '\0', sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(server_fd, (struct sockaddr *)&server, sizeof(server)) < 0)
{
perror("Could not bind socket");
close(server_fd);
return 1;
}
if (listen(server_fd, 1) < 0)
{
perror("Could not listen on socket");
close(server_fd);
return 1;
}
printf("Server TCP is listening on port %d ... \n", port);
socklen_t client_len = sizeof(client);
client_fd = accept(server_fd, (struct sockaddr *)&client, &client_len);
if (client_fd < 0)
{
perror("Could not establish new connection");
close(server_fd);
return 1;
}
remote_port = ntohs(client.sin_port);
inet_ntop(AF_INET, &client.sin_addr, remote_ip, sizeof(remote_ip));
printf("Client IP address: %s, port %d\n", remote_ip, remote_port);
while (1)
{
read = recv_all(client_fd, buffer, BUFFER_SIZE);
if (read <= 0)
{
if (read < 0)
perror("Client read failed");
else
printf("Client disconnected\n");
break;
}
if ((res = strcmp(buffer, "SENDFILE_TXT\n") == 0))
{
printf("-------FROM CLIENT: %s-------\n", buffer);
memset(buffer, BUFFER_SIZE, '\0');
strcpy(buffer, "OK");
if (send_all(client_fd, buffer, BUFFER_SIZE) < 0)
{
perror("Client write failed");
break;
}
read = recv_all(client_fd, buffer, BUFFER_SIZE);
if (read <= 0)
{
if (read < 0)
perror("Client read failed");
else
printf("Client disconnected\n");
break;
}
printf("-------FROM CLIENT: %s-------\n", buffer);
memset(filename, '\0', BUFFER_SIZE);
strcpy(filename, buffer);
memset(buffer, BUFFER_SIZE, '\0');
strcpy(buffer, "OK");
if (send_all(client_fd, buffer, BUFFER_SIZE) < 0)
{
perror("Client write failed");
break;
}
recv_txt_file(client_fd, BUFFER_SIZE, filename);
}
else
{
printf("FROM CLIENT: %.*s\n", BUFFER_SIZE, buffer);
if (send_all(client_fd, buffer, BUFFER_SIZE) < 0)
{
perror("Client write failed");
break;
}
}
}
close(client_fd);
close(server_fd);
return 0;
}
client.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>
#define BUFFER_SIZE 1024
socklen_t hostname_to_ip_port(char *hostname, int port, struct sockaddr_storage *addr)
{
int sockfd;
struct addrinfo hints, *servinfo;
int rv;
char service[20];
sprintf(service, "%d", port);
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
if ((rv = getaddrinfo(hostname, service, &hints, &servinfo)) != 0)
{
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 0;
}
socklen_t addrlen = servinfo->ai_addrlen;
memcpy(addr, servinfo->ai_addr, addrlen);
freeaddrinfo(servinfo);
return addrlen;
}
int send_all(int sockfd, const char *buf, size_t len)
{
ssize_t n;
while (len > 0)
{
n = send(sockfd, buf, len, 0);
if (n < 0)
return -1;
buf += n;
len -= n;
}
return 0;
}
int recv_all(int sockfd, char *buf, int len)
{
ssize_t n;
while (len > 0)
{
n = recv(sockfd, buf, len, 0);
if (n <= 0)
return n;
buf += n;
len -= n;
}
return 1;
}
long int get_file_size(char filename[])
{
// opening the file in read mode
FILE *fp = fopen(filename, "r");
// checking if the file exist or not
if (fp == NULL) {
printf("File Not Found!\n");
return -1;
}
fseek(fp, 0L, SEEK_END);
// calculating the size of the file
long int res = ftell(fp);
// closing the file
fclose(fp);
return res;
}
int send_txt_file(int sockfd, int len, const char *filename)
{
FILE *fp = fopen(filename, "r");
int b;
char buffer[BUFFER_SIZE];
memset(buffer, '\0', BUFFER_SIZE);
if (fp == NULL)
{
perror("Error while openning file");
return 0;
}
while ((b = fread(buffer, 1, BUFFER_SIZE, fp)) > 0){
send_all(sockfd, buffer, BUFFER_SIZE);
memset(buffer, '\0', BUFFER_SIZE);
}
fclose(fp);
return 1;
}
int main(int argc, char **argv)
{
char *hostname = "127.0.0.1";
int port = 6666;
char buffer[BUFFER_SIZE], fname[BUFFER_SIZE];
int sockfd, err, res;
struct sockaddr_storage server_addr;
socklen_t server_addr_len;
server_addr_len = hostname_to_ip_port(hostname, port, &server_addr);
if (server_addr_len == 0)
return 1;
sockfd = socket(server_addr.ss_family, SOCK_STREAM, IPPROTO_TCP);
if (sockfd < 0)
{
perror("Could not create socket");
return 1;
}
if (connect(sockfd, (struct sockaddr *)&server_addr, server_addr_len) < 0)
{
perror("Could not connect socket");
return 1;
}
while (1)
{
memset(buffer, BUFFER_SIZE, '\0');
memset(fname, BUFFER_SIZE, '\0');
printf("> ");
if (!fgets(buffer, BUFFER_SIZE, stdin))
break;
if (strstr(buffer, "SENDFILE_TXT") != NULL)
{
if (send_all(sockfd, buffer, BUFFER_SIZE) < 0)
{
perror("Could not send message");
close(sockfd);
return 1;
}
memset(buffer, BUFFER_SIZE, '\0');
err = recv_all(sockfd, buffer, BUFFER_SIZE);
if (err <= 0)
{
if (err < 0)
perror("Could not read message");
else
printf("Server disconnected\n");
break;
}
if ((res = strcmp(buffer, "OK") == 0))
{
printf("-------FROM SERVER: %s-------\n", buffer);
printf("Give filename> ");
memset(fname, BUFFER_SIZE, '\0');
if (!fgets(fname, BUFFER_SIZE, stdin))
break;
if (send_all(sockfd, fname, BUFFER_SIZE) < 0)
{
perror("Could not send message");
close(sockfd);
return 1;
}
}
err = recv_all(sockfd, buffer, BUFFER_SIZE);
if (err <= 0)
{
if (err < 0)
perror("Could not read message");
else
printf("Server disconnected\n");
break;
}
fname[strlen(fname)-1] = 0;
printf("----%s----\n", fname);
send_txt_file(sockfd, BUFFER_SIZE, fname);
printf("FROM SERVER: %.*s\n", BUFFER_SIZE, buffer);
}
else
{
if (send_all(sockfd, buffer, BUFFER_SIZE) < 0)
{
perror("Could not send message");
close(sockfd);
return 1;
}
err = recv_all(sockfd, buffer, BUFFER_SIZE);
if (err <= 0)
{
if (err < 0)
perror("Could not read message");
else
printf("Server disconnected\n");
break;
}
printf("FROM SERVER: %.*s\n", BUFFER_SIZE, buffer);
}
}
close(sockfd);
return 0;
}
The way you are using send_txt_file() and recv_txt_file() to transfer a file is not correct.
On the server side:
When a client connects, the server waits for the client to send exactly BUFFER_SIZE (1024) bytes for each command, no more, no less (which is a waste of bandwidth for short commands). When a SENDFILE_TXT command is received, the server reads the filename from the client (which will cause a buffer overflow if the filename is exactly BUFFER_SIZE bytes in length), and then calls recv_txt_file().
recv_txt_file() attempts to read from the client in a loop, reading in exactly BUFFER_SIZE chunks. However, the while loop being used is coded incorrectly. It is checking the return value of recv_all() for failure, not for success. The != check needs to be changed to == instead. And also, the b variable that is being used to increment total, and tell fwrite() how many bytes to write, is never set to any value other than 0. It needs to be set to BUFFER_SIZE instead, since that is how many bytes have actually been read if recv_all() returns 1.
However, even if the while loop were coded properly, the transfer would still not operate properly, because it requires the file to be sent in even multiples of BUFFER_SIZE. If the file is not an even multiple in size, recv_all() will end up waiting for data that the client does not send, until an error occurs on the socket.
Also, recv_txt_file() is closing the connection after the transfer is finished. It should not do that, as that will prevent the client from being able to send further commands after sending a file. The client is not closing its end of the connection after sending a file, so the server should not be closing its end after receiving a file.
On the client side:
When the client sends a SENDFILE_TXT command and gets an acknowledgement back, it calls send_txt_file(), which reads the file in a loop, sending it to the server in BUFFER_SIZE sized chunks. If the file size is not an even multiple of BUFFER_SIZE, the last block sent will just waste bandwidth and send random garbage to the server. Also, you are ignoring the return value of send_all() to break the loop if an error occurs on the socket.
The client should send the actual file size to the server before sending the file data. The server can then read that size value first so it knows when to stop reading.
That being said, try something more like the following:
server.c
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>
#include <string.h>
#include <stdint.h>
#define BUFFER_SIZE 1024
int send_all(int sockfd, const void *buf, int len)
{
ssize_t n;
const char *pbuf = (const char*) buf;
while (len > 0)
{
n = send(sockfd, pbuf, len, 0);
if (n < 0)
{
perror("Client write failed");
return n;
}
pbuf += n;
len -= n;
}
return 0;
}
int send_uint32(int sockfd, uint32_t value)
{
value = htonl(value);
if (send_all(sockfd, &value, sizeof(value)) < 0)
return -1;
return 0;
}
int send_str(int sockfd, const char *s)
{
uint32_t len = strlen(s);
int res = send_uint32(sockfd, len);
if (res == 0)
res = send_all(sockfd, s, len);
return res;
}
int recv_all(int sockfd, void * buf, int len)
{
ssize_t n;
char *pbuf = (char*) buf;
while (len > 0)
{
n = recv(sockfd, pbuf, len, 0);
if (n <= 0)
{
if (n < 0)
perror("Client read failed");
else
printf("Client disconnected\n");
return n;
}
pbuf += n;
len -= n;
}
return 1;
}
int recv_uint32(int sockfd, uint32_t *value)
{
int res = recv_all(sockfd, value, sizeof(*value));
if (res > 0)
*value = ntohl(*value);
return res;
}
int recv_uint64(int sockfd, uint64_t *value)
{
int res = recv_all(sockfd, value, sizeof(*value));
if (res > 0)
*value = ntohll(*value); // <-- use any implementation of your choosing...
return res;
}
int recv_str(int sockfd, char **str)
{
uint32_t len;
int res = recv_uint32(sockfd, &len);
if (res <= 0)
return res;
*str = (char*) malloc(len + 1);
if (*str == NULL)
{
perror("Could not allocate memory");
return -1;
}
res = recv_all(sockfd, *str, len);
if (res <= 0)
free(*str);
else
(*str)[len] = '\0';
return res;
}
int recv_txt_file(int sockfd)
{
char *filename;
uint64_t filesize;
if (recv_str(sockfd, &filename) <= 0)
return -1;
int res = recv_uint64(sockfd, &filesize);
if (res <= 0)
{
free(filename);
return -1;
}
printf("-------FROM CLIENT: %s-------\n", filename);
FILE* fp = fopen(filename, "wb");
if (fp == NULL)
{
perror("Could not create file");
free(filename);
send_str(sockfd, "NO");
return 0;
}
free(filename);
// optional: pre-size the new file to the specified filesize...
if (send_str(sockfd, "OK") < 0)
return -1;
char buffer[BUFFER_SIZE];
int b;
uint64_t total = 0;
while (filesize > 0)
{
b = (filesize > BUFFER_SIZE) ? BUFFER_SIZE : (int) filesize;
res = recv_all(sockfd, buffer, b);
if (res <= 0)
{
fclose(fp);
return -1;
}
if (fwrite(buffer, b, 1, fp) < 1)
{
perror("Could not write to file");
fclose(fp);
send_str(sockfd, "ERROR");
return -1;
}
total += b;
filesize -= b;
}
fclose(fp);
printf("Received bytes: %lu\n", total);
if (send_str(sockfd, "OK") < 0)
return -1;
return 1;
}
int main()
{
int port = 6666;
int server_fd, client_fd, read;
struct sockaddr_in server, client;
char filename[BUFFER_SIZE], *cmd;
char remote_ip[16];
int remote_port, res = 0;
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd < 0)
{
perror("Could not create socket");
return 1;
}
int optval = 1;
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(int));
memset(&server, '\0', sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(server_fd, (struct sockaddr *) &server, sizeof(server)) < 0)
{
perror("Could not bind socket");
close(server_fd);
return 1;
}
if (listen(server_fd, 1) < 0)
{
perror("Could not listen on socket");
close(server_fd);
return 1;
}
printf("Server TCP is listening on port %d ... \n", port);
socklen_t client_len = sizeof(client);
client_fd = accept(server_fd, (struct sockaddr *) &client, &client_len);
if (client_fd < 0)
{
perror("Could not establish new connection");
close(server_fd);
return 1;
}
remote_port = ntohs(client.sin_port);
inet_ntop(AF_INET, &client.sin_addr, remote_ip, sizeof(remote_ip));
printf("Client IP address: %s, port %d\n", remote_ip, remote_port);
while (recv_str(client_fd, &cmd) > 0)
{
printf("-------FROM CLIENT: %s-------\n", cmd);
if (strcmp(cmd, "SENDFILE_TXT") == 0)
{
if (recv_txt_file(client_fd) < 0)
break;
}
else
{
if (send_str(client_fd, cmd) < 0)
{
free(cmd);
break;
}
}
free(cmd);
}
close(client_fd);
close(server_fd);
return 0;
}
client.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>
#include <stdint.h>
#define BUFFER_SIZE 1024
socklen_t hostname_to_ip_port(char *hostname, int port, struct sockaddr_storage *addr)
{
int sockfd;
struct addrinfo hints, *servinfo;
int rv;
char service[20];
sprintf(service, "%d", port);
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
if ((rv = getaddrinfo(hostname, service, &hints, &servinfo)) != 0)
{
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 0;
}
socklen_t addrlen = servinfo->ai_addrlen;
memcpy(addr, servinfo->ai_addr, addrlen);
freeaddrinfo(servinfo);
return addrlen;
}
int send_all(int sockfd, const void *buf, size_t len)
{
ssize_t n;
const char *pbuf = (const char *) buf;
while (len > 0)
{
n = send(sockfd, pbuf, len, 0);
if (n < 0)
{
perror("Server write failed");
return n;
}
pbuf += n;
len -= n;
}
return 0;
}
int send_uint32(int sockfd, uint32_t value)
{
value = htonl(value);
return send_all(sockfd, &value, sizeof(value));
}
int send_uint64(int sockfd, uint64_t value)
{
value = htonll(value); // <-- use any implementation of your choosing...
return send_all(sockfd, &value, sizeof(value));
}
int send_str(int sockfd, const char *s)
{
uint32_t len = strlen(s);
int res = send_uint32(sockfd, len);
if (res == 0)
res = send_all(sockfd, s, len);
return res;
}
int recv_all(int sockfd, void *buf, int len)
{
ssize_t n;
char *pbuf = (char*) buf;
while (len > 0)
{
n = recv(sockfd, pbuf, len, 0);
if (n <= 0)
{
if (n < 0)
perror("Server read failed");
else
printf("Server disconnected\n");
return n;
}
pbuf += n;
len -= n;
}
return 1;
}
int recv_uint32(int sockfd, uint32_t *value)
{
int res = recv_all(sockfd, value, sizeof(*value));
if (res > 0)
*value = ntohl(*value);
return res;
}
int recv_str(int sockfd, char **str)
{
uint32_t len;
int res = recv_uint32(sockfd, &len);
if (res <= 0)
return res;
*str = (char*) malloc(len + 1);
if (*str == NULL)
{
perror("Could not allocate memory");
return -1;
}
res = recv_all(sockfd, *str, len);
if (res <= 0)
free(*str);
else
(*str)[len] = '\0';
return res;
}
int send_txt_file(int sockfd, const char *filename)
{
char *resp;
int res;
long int filesize;
char buffer[BUFFER_SIZE];
int b;
FILE* fp = fopen(filename, "rb");
if (fp == NULL)
{
printf("Could not open file!\n");
return 0;
}
fseek(fp, 0L, SEEK_END);
filesize = ftell(fp);
fseek(fp, 0L, SEEK_SET);
if (filesize < 0)
{
fclose(fp);
return 0;
}
if (send_str(sockfd, "SENDFILE_TXT") < 0)
{
fclose(fp);
return -1;
}
if (send_str(sockfd, filename) < 0)
{
fclose(fp);
return -1;
}
if (send_uint64(sockfd, filesize) < 0)
{
fclose(fp);
return -1;
}
res = recv_str(sockfd, &resp);
if (res <= 0)
return -1;
printf("-------FROM SERVER: %s-------\n", resp);
if (strcmp(resp, "OK") != 0)
{
free(resp);
fclose(fp);
return 0;
}
free(resp);
while (filesize > 0)
{
b = (filesize > BUFFER_SIZE) ? BUFFER_SIZE : (int) filesize;
b = fread(buffer, 1, b, fp);
if (b < 1)
{
fclose(fp);
return -1;
}
if (send_all(sockfd, buffer, b) < 0)
{
fclose(fp);
return -1;
}
filesize -= b;
}
fclose(fp);
res = recv_str(sockfd, &resp);
if (res <= 0)
return -1;
printf("-------FROM SERVER: %s-------\n", resp);
free(resp);
return 0;
}
int prompt(const char *text, char **input)
{
*input = NULL;
size_t size = 0;
printf("%s> ", text);
ssize_t len = getline(input, &size, stdin);
if (len < 0)
return len;
if ((*input)[len-1] == '\n')
{
--len;
(*input)[len] = '\0';
}
return len;
}
int main()
{
char *hostname = "127.0.0.1";
int port = 6666;
char *cmd, *resp, *fname;
size_t size;
ssize_t len;
int sockfd, res;
struct sockaddr_storage server_addr;
socklen_t server_addr_len;
server_addr_len = hostname_to_ip_port(hostname, port, &server_addr);
if (server_addr_len == 0)
return 1;
sockfd = socket(server_addr.ss_family, SOCK_STREAM, IPPROTO_TCP);
if (sockfd < 0)
{
perror("Could not create socket");
return 1;
}
if (connect(sockfd, (struct sockaddr *) &server_addr, server_addr_len) < 0)
{
perror("Could not connect socket");
return 1;
}
while (prompt("", &cmd) >= 0)
{
if (strcmp(cmd, "SENDFILE_TXT") == 0)
{
if (prompt("Give filename", &fname) < 0)
break;
if (send_txt_file(sockfd, fname) < 0)
break;
free(fname);
}
else
{
if (send_str(sockfd, cmd) < 0)
{
free(cmd);
close(sockfd);
return 1;
}
if (recv_str(sockfd, &resp) <= 0)
break;
printf("FROM SERVER: %s\n", resp);
free(resp);
}
free(cmd);
}
close(sockfd);
return 0;
}
regarding:
int total = 0, b = 0;
and
while(recv_all(sockfd, buffer, len) != 1)
{
total += b;
fwrite(buffer, 1, b, fp);
the total and b never move from their initial value of 0. So 0 bytes are written to the output file.
I wanted to write a simple TCP echo server application. I managed to do the echo part, but have some problems with sending files between client and server. The idea is simple: despite sending ordinary messages, client can send a special command to server (\SENDFILE filename.txt), and after receiving such command, server should ask client for this file, and get the file from client. (Further I would like to get a file from one client, and later send it to another one).
I think "the protocol" here is simple, however, after typing \SENDFILE at client's side, client hangs up, and does not receive any further messages from server. Moreover (server and client are in different directories) at server's side there's only an empty file from client, with no content inside.
Any ideas what can be wrong here?
client.c
#include<stdio.h> //printf
#include<string.h> //
#include <sys/stat.h>
#include<sys/socket.h> //socket
#include<arpa/inet.h> //inet_addr
#include <fcntl.h>
#define SERVER_PORT 9034
#define BUFF_SIZE 2000
int sendall(int s, char *buf, int len)
{
int total = 0;
int bytesleft = len;
int n;
while(total < len)
{
n = send(s, buf+total, bytesleft, 0);
if (n == -1)
break;
total += n;
bytesleft -= n;
}
return n==-1?-1:0;
}
void SendMsgToSender(char *msg, int connfd)
{
write(connfd, msg, strlen(msg));
memset(msg, 0, BUFF_SIZE);
}
int main(int argc , char *argv[])
{
int sock;
struct sockaddr_in server;
char bufferOUT[BUFF_SIZE] , bufferIN[BUFF_SIZE];
struct stat file_stat;
memset(bufferOUT, 0, BUFF_SIZE);
memset(bufferIN, 0, BUFF_SIZE);
//Create socket
sock = socket(AF_INET , SOCK_STREAM , 0);
if (sock == -1)
{
printf("Could not create socket");
}
// puts("Socket created");
server.sin_addr.s_addr = inet_addr("127.0.0.1");
server.sin_family = AF_INET;
server.sin_port = htons( SERVER_PORT );
//Connect to remote server
if (connect(sock , (struct sockaddr *)&server , sizeof(server)) < 0)
{
perror("Connect failed. Error");
return 1;
}
// puts("Connected\n");
int read_size = 10;
//keep communicating with server
while(1)
{
printf("> ");
fgets(bufferOUT, BUFF_SIZE, stdin);
//Send some data
if( send(sock , bufferOUT , BUFF_SIZE , 0) < 0)
{
perror("Send failed");
return 1;
}
//Receive a reply from the server
if( (read_size = recv(sock , bufferIN , BUFF_SIZE , 0)) < 0)
{
perror("Recv failed");
break;
}
if(read_size == 0)
break;
if(bufferIN[0] == '\\')
{
char tmp[BUFF_SIZE], filename[BUFF_SIZE], *param;
memset(filename, BUFF_SIZE, 0);
strcpy(tmp, bufferIN);
param = strtok(tmp, " ");
if(param != NULL)
{
if(!strcmp(param, "\\GIVEMEFILE"))
{
param = strtok(NULL, " ");
if(param != NULL)
{
strcpy(filename, param);
FILE * fp;
int nBytes;
char buffer[BUFF_SIZE], *s;
memset(buffer, 0, BUFF_SIZE);
fp = fopen(filename, "r");
if(fp == NULL)
{
perror("fopen");
fflush(stdout);
break;
}
int remain_data = file_stat.st_size;
do
{
s = fgets(buffer, BUFF_SIZE, fp);
if(s != NULL && buffer[0] != EOF)
{
nBytes = sendall(sock, buffer, BUFF_SIZE);
remain_data -= nBytes;
}
else
break;
}
while((s != NULL) && (nBytes > 0) && (remain_data > 0));
fclose(fp);
memset(bufferOUT, 0, BUFF_SIZE);
memset(bufferIN, 0, BUFF_SIZE);
continue;
}
}
}
}
else
{
printf("%s\n", bufferIN);
fflush(stdout);
}
memset(bufferOUT, 0, BUFF_SIZE);
memset(bufferIN, 0, BUFF_SIZE);
}
close(sock);
return 0;
}
server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <time.h>
#include <fcntl.h>
#define SERVER_PORT 9034
#define BUFF_SIZE 2000
void StripNewline(char *s)
{
while(*s != '\0')
{
if(*s == '\r' || *s == '\n')
{
*s = '\0';
}
s++;
}
}
void SendMsgToSender(char *msg, int connfd)
{
write(connfd, msg, strlen(msg));
memset(msg, 0, BUFF_SIZE);
}
// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET)
{
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int GetFileFromClient(int connfd, char *filename)
{
FILE * fp = NULL;
int bytes;
char buffer[BUFF_SIZE];
memset(buffer, 0, BUFF_SIZE);
fp = fopen(filename, "w");
if(fp == NULL)
return 0;
memset(buffer, 0, BUFF_SIZE);
sprintf(buffer, "\\GIVEMEFILE %s \r\n", filename);
SendMsgToSender(buffer, connfd);
while(1)
{
memset(buffer ,0 , BUFF_SIZE);
if((bytes = recv(connfd , buffer , BUFF_SIZE , 0) ) <= 0)
return 0;
else
fprintf(fp, "%s\n", buffer);
}
fclose(fp);
sleep(1);
memset(buffer, 0, BUFF_SIZE);
sprintf(buffer, "\r\n");
SendMsgToSender(buffer, connfd);
return 1;
}
int main(void)
{
fd_set master;
fd_set read_fds;
int fdmax;
int listener;
int client_sock;
struct sockaddr_storage remoteaddr;
socklen_t addrlen;
char bufferIN[BUFF_SIZE], bufferOUT[BUFF_SIZE], tmp[BUFF_SIZE], *datetime;
int nbytes;
char remoteIP[INET6_ADDRSTRLEN];
int yes=1;
int i, j, rv;
struct addrinfo hints, *ai, *p;
FD_ZERO(&master);
FD_ZERO(&read_fds);
memset(bufferIN, 0, BUFF_SIZE);
memset(bufferOUT, 0, BUFF_SIZE);
memset(tmp, 0, BUFF_SIZE);
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
char port[16] = "9034";
if (getaddrinfo(NULL, port, &hints, &ai) < 0)
{
fprintf(stderr, "selectserver: %s\n", gai_strerror(rv));
exit(1);
}
for(p = ai; p != NULL; p = p->ai_next)
{
listener = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if (listener < 0)
{
continue;
}
setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
if (bind(listener, p->ai_addr, p->ai_addrlen) < 0)
continue;
break;
}
if (p == NULL)
exit(2);
freeaddrinfo(ai);
if (listen(listener, 10) == -1)
{
perror("listen");
exit(3);
}
FD_SET(listener, &master);
fdmax = listener;
printf("Server is running ...\n\n");
for(;;)
{
read_fds = master;
if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1)
{
perror("select");
exit(4);
}
for(i = 0; i <= fdmax; i++)
{
if (FD_ISSET(i, &read_fds))
{
if (i == listener)
{
addrlen = sizeof remoteaddr;
client_sock = accept(listener,
(struct sockaddr *)&remoteaddr,
&addrlen);
if (client_sock == -1)
{
perror("accept");
}
else
{
FD_SET(client_sock, &master);
if (client_sock > fdmax)
fdmax = client_sock;
}
}
else
{
if ((nbytes = recv(i, bufferIN, BUFF_SIZE, 0)) <= 0)
{
if (nbytes == 0)
close(i);
else if(nbytes == -1)
{
perror("recv");
fflush(stdout);
}
close(i);
FD_CLR(i, &master);
}
else
{
bufferIN[nbytes-1] = '\0';
StripNewline(bufferIN);
strcpy(tmp, bufferIN);
if(bufferIN[0] == '\\')
{
char *command, *param;
command = strtok(bufferIN, " ");
if(!strcmp(command, "\\QUIT"))
{
close(i);
FD_CLR(i, &master);
break;
}
else if(!strcmp(command, "\\SENDFILE"))
{
param = strtok(tmp, " ");
if(param != NULL)
{
param = strtok(NULL, " ");
if(param != NULL)
{
printf("Client is sending me a file '%s'...\n", param);
GetFileFromClient(i, param);
}
}
}
else
{
SendMsgToSender(bufferIN, i);
}
memset(bufferIN, 0, BUFF_SIZE);
memset(bufferOUT, 0, BUFF_SIZE);
}
else
{
SendMsgToSender(bufferIN, i);
}
}
} // END handle data from client
} // END got new incoming connection
} // END looping through file descriptors
} // END for(;;)
memset(bufferIN, 0, BUFF_SIZE);
memset(bufferOUT, 0, BUFF_SIZE);
return 0;
}
strcpy(tmp, bufferIN);
Here you are assuming that whatever was read was null-terminated.
param = strtok(tmp, " ");
if(param != NULL)
{
if(!strcmp(param, "\\GIVEMEFILE"))
Here you are assuming that an entire message has been received.
strcpy(filename, param);
Ditto.
memset(buffer, 0, BUFF_SIZE);
Pointless. Remove.
do
{
s = fgets(buffer, BUFF_SIZE, fp);
Here you are assuming that the file consists of lines.
if(s != NULL && buffer[0] != EOF)
Testing buffer[0] !=EOF is meaningless. If you had reached EOF, s would have been null, assuming the file consists of lines, but there is nothing about a line that says anything about what its first character can be, other than that it isn't a line terminator.
memset(bufferOUT, 0, BUFF_SIZE);
memset(bufferIN, 0, BUFF_SIZE);
Both pointless. Remove.
memset(bufferOUT, 0, BUFF_SIZE);
memset(bufferIN, 0, BUFF_SIZE);
Ditto.
void StripNewline(char *s)
This method appears completely pointless. Remove.
void SendMsgToSender(char *msg, int connfd)
{
write(connfd, msg, strlen(msg));
Here you are sending a string to the peer without the trailing null, which the peer is looking for at strlen() above. Have a good think about what your application protocol actually entails.
memset(msg, 0, BUFF_SIZE);
Pointless. Remove.
int GetFileFromClient(int connfd, char *filename)
{
FILE * fp = NULL;
int bytes;
char buffer[BUFF_SIZE];
memset(buffer, 0, BUFF_SIZE);
Pointless. Remove.
memset(buffer, 0, BUFF_SIZE);
Ditto.
sprintf(buffer, "\\GIVEMEFILE %s \r\n", filename);
SendMsgToSender(buffer, connfd);
while(1)
{
memset(buffer ,0 , BUFF_SIZE);
Pointless. Remove.
if((bytes = recv(connfd , buffer , BUFF_SIZE , 0) ) <= 0)
return 0;
Here you need to distinguish between (1) bytes == 0, which means the peer disconnected, and (2) byte == -1, which indicates an error, which you need to log, via errno, strerror(), and friends.
else
fprintf(fp, "%s\n", buffer);
Change to fprintf(fp, "%.*s\n", bytes, buffer). You are assuming throughout that all messages are null-terminated by TCP. They aren't.
sleep(1);
Pointless. Remove.
memset(buffer, 0, BUFF_SIZE);
Ditto.
sprintf(buffer, "\r\n");
SendMsgToSender(buffer, connfd);
Sending a line terminator to the peer appears completely pointless.
memset(bufferIN, 0, BUFF_SIZE);
memset(bufferOUT, 0, BUFF_SIZE);
memset(tmp, 0, BUFF_SIZE);
All pointless. Remove.
if (bind(listener, p->ai_addr, p->ai_addrlen) < 0)
continue;
Here you need to print an error mesage instead of just ignoring the condition.
if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1)
You haven't put the listening socket into non-blocking mode. Using select() is therefore pointless.
bufferIN[nbytes-1] = '\0';
StripNewline(bufferIN);
Why?
strcpy(tmp, bufferIN);
Why? What's wrong with continuing to use bufferIN?
if(bufferIN[0] == '\\')
{
char *command, *param;
command = strtok(bufferIN, " ");
Here again you are assuming a complete command was received, complete with trailing null.
memset(bufferIN, 0, BUFF_SIZE);
memset(bufferOUT, 0, BUFF_SIZE);
Both pointless. Remove. This is jut cargo-cult programming. recv() returns a length. Use it.
memset(bufferIN, 0, BUFF_SIZE);
memset(bufferOUT, 0, BUFF_SIZE);
Ditto, in spades.
Basically you have an application protocol problem. Specifically, you don't have an application protocol. Just a whole lot of unwarranted assumptions. If you want a trailing null, (a) send a trailing null, and (b) loop reading until you receive it. You also have an assumption about the content of the files being sent, which is completely unnecessary. Just read bytes from the file and send them to the server. No assumption about lines or line terminators necessary. If you're sending multiple files over the same connection you will need to send the file size ahead of the file, so the receiver will know exactly how many bytes to read and copy to the file.
In essence, you need to rethink this completely.
In client.c you have to initialize file_statbefore getting the size of the file stat(filename, &file_stat);
Because of this error remain_data will always have a wrong value.
In Server.c
Because of the error in the while loop as pointed out by EJP you are overwriting the file sent by the client. Basically making it empty.
Open the client filename with "r" option.
Open another file in the server and receive data to that file.
Small example to receive data of files within BUFF_SIZE. You can use the some logic and expand it to bigger files like its done in Client.c
fd = fopen(<new_file_path>, "w");
while(1)
{
memset(buffer ,0 , BUFF_SIZE);
if((bytes = recv(connfd , buffer , BUFF_SIZE , 0) ) == BUFF_SIZE)
break;
}
fprintf(fd, "%s\n", buffer);
fclose(fd);
I just implemented a HTTP/1.1 client to parse the chunked transferring coding. However, it works for some websites but fails for others. I assume I need to read chunkSize + 2 bytes including \r\n for each chunk data, am I right?
Here is my code:
while(chunked)//if detecting chunked in the header before, this is true
{
//getLine is a function can read a line separated by \r\n
//sockfd is a socket created before and file position is at the start of HTTP body (after that blank line between header and body)
line = getLine(sockfd);
printf("%s", line);//print the chunk size line in hex
int chunkSize = strtol(line, NULL, 16);
if(chunkSize == 0)
{
printf("##### Read chunk size of 0, reading until we hit end of stream.\n");
break;
}
printf("##### Chunk size (in hex above) is %d in decimal and is printed here:\n", chunkSize);
char* chunkBuf = (char *)malloc(chunkSize + 2 + 1);//2 for \r\n, 1 for \0
bzero(chunkBuf, chunkSize + 3);
if(read(sockfd, chunkBuf, chunkSize + 2) == 0)//sockfd is a socket created before
{
perror("Read Error: ");
exit(EXIT_FAILURE);
}
printf("%s", chunkBuf);//print the chunk content
free(chunkBuf);
}
Actually I can print out the whole content without parsing, i.e. print line by line, so I think I may make some mistakes in the code above, could anyone give me some hint?
Below is the whole code for your reference:
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <netdb.h>
#include <netinet/in.h>
#include <string.h>
#include <stdbool.h>
#include <unistd.h>
#define HTTP_VERSION "HTTP/1.1"
#define PAGE "/"
int createSokect()
{
int socketfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(socketfd < 0)
{
perror("Cannot create socket\n");
exit(EXIT_FAILURE);
}
return socketfd;
}
char* getIP(char* host)
{
struct hostent* hent;
int len = 15;//xxx.xxx.xxx.xxx
char *ipaddr = (char *)malloc(len + 1);//one more \0
bzero(ipaddr, len + 1);
if((hent = gethostbyname(host)) == NULL)
{
printf("Cannot get IP for this host: %s\n", host);
exit(EXIT_FAILURE);
}
if(inet_ntop(AF_INET, (void*)hent->h_addr_list[0], ipaddr, len) == NULL)
{
printf("Cannot resolve IP for this host: %s\n", host);
exit(EXIT_FAILURE);
}
return ipaddr;
}
char* createQuery(char* host, char* page)
{
char* msg = "GET %s %s\r\nHost: %s\r\nConnection: close\r\n\r\n";
char* query = (char *)malloc(strlen(host) + strlen(page) + strlen(msg) + strlen(HTTP_VERSION) - 6 + 1);//-6: %s %s %s
sprintf(query, msg, page, HTTP_VERSION, host);
return query;
}
char* getLine(int fd)
{
char c = 0, pre = 0;
char* line = 0;
int size = 1;
int pos = 0;
while(read(fd, &c, 1)!=0)
{
if(pos + 1 == size)
{
size *= 2;
line = realloc(line, size);
}
line[pos++] = c;
//printf("%c", c);
if(pre == '\r' && c == '\n')//this is a new line
{
break;
}
pre = c;
}
if(line)
{
line[pos++] = 0;
}
return line;
}
int main(int argc, char** argv)
{
if(argc < 3)
{
perror("Need more arguments");
exit(EXIT_FAILURE);
}
int sockfd = createSokect();
char* ip = getIP(argv[1]);
printf("Host: %s\n", argv[1]);
printf("IP: %s\n", ip);
struct sockaddr_in server;
server.sin_family = AF_INET;
int err = inet_pton(server.sin_family, ip, (void *)(&(server.sin_addr.s_addr)));
if(err != 1)
{
perror("Cannot convert IP to binary address\n");
exit(EXIT_FAILURE);
}
server.sin_port = htons(atoi(argv[2]));
printf("port: %d\n", server.sin_port);
//connect to the server
if(connect(sockfd, (struct sockaddr *)&server, sizeof(server)) < 0)
{
printf("Cannot connect: %d\n", err);
exit(EXIT_FAILURE);
}
char* query = createQuery(argv[1], PAGE);
printf("##### CLIENT IS SENDING THE FOLLOWING TO SERVER:\n");
printf("%s", query);
int offset = 0;
//send query to the server
err = send(sockfd, query + offset, strlen(query) - offset, 0);
if(err < 0)
{
perror("Cannot send query");
exit(EXIT_FAILURE);
}
printf("##### CLIENT RECEIVED THE FOLLOWING FROM SERVER:\n");
//receive message line by line
bool chunked = false;
char* line;
while((line = getLine(sockfd)) != NULL)
{
printf("%s", line);
if(!strcasecmp(line, "transfer-encoding: chunked\r\n"))
{
chunked = true;
//printf("Chunked here\n");
}
if(!strcmp(line, "\r\n"))
{
printf("##### Just read blank line, now reading body.\n");
if(chunked)//chunked, we print those in another way, otherwise line by line
{
free(line);
break;
}
}
free(line);
}
while(chunked)
{
line = getLine(sockfd);
printf("%s", line);
int chunkSize = strtol(line, NULL, 16);
if(chunkSize == 0)
{
printf("##### Read chunk size of 0, reading until we hit end of stream.\n");
break;
}
printf("##### Chunk size (in hex above) is %d in decimal and is printed here:\n", chunkSize);
char* chunkBuf = (char *)malloc(chunkSize + 2 + 1);//2 for \r\n, 1 for \0
bzero(chunkBuf, chunkSize + 3);
if(read(sockfd, chunkBuf, chunkSize + 2) == 0)
{
perror("Read Error: ");
exit(EXIT_FAILURE);
}
printf("%s", chunkBuf);
free(chunkBuf);
}
//receive message from the server
/*
char buf[2048];
bzero(buf, sizeof(buf));
err = recv(sockfd, buf, sizeof(buf), 0);
if(err < 0)
{
perror("Receive error");
exit(EXIT_FAILURE);
}
char *content = buf;
fprintf(stdout, content);*/
free(query);
free(ip);
close(sockfd);
printf("##### Connection closed by server.\n");
exit(EXIT_SUCCESS);
}
The line:
if(read(sockfd, chunkBuf, chunkSize + 2) == 0) ...
will read up to chunkSize+2, i.e. it can read less. See the manual page of read. Your code shall look something like:
int n = 0;
while (n<chunkSize) {
r = read(sockfd, chunkBuf+n, chunkSize - n);
if (r <= 0) { error or closed conection ... }
n += r;
}
Since I know the chunk size, so I read character one by one counting up to the chunk size. This way can work. But I still don't understand why I failed when trying to use read or recv by the whole chunk size at one time.
I'm playing around with C although I am fairly new to it. to find out how threads and locks work, connections work, within an IRC.
However I have come across and error, and I'm not sure how to fix it.
Bellow are the errors I get.
sample.c: In function ‘clientThreadEntry’:
sample.c:343:5: warning: passing argument 1 of ‘connectionMain’ makes pointer from integer without a cast [enabled by default]
connectionMain(t->sock);
^
sample.c:216:5: note: expected ‘struct client_thread *’ but argument is of type ‘int’
int connectionMain(struct client_thread *t) {
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <string.h>
#include <strings.h>
#include <signal.h>
#include <netdb.h>
#include <time.h>
#include <errno.h>
#include <pthread.h>
#include <ctype.h>
struct client_thread {
pthread_t thread;
int thread_id;
int sock;
char nickname[32];
int state;
#define DEAD 1
#define ALIVE 2
int user_command_seen;
int user_has_registered;
time_t timeout;
char line[1024];
int line_len;
int next_message;
};
/**
Allocate static structure for all client connections.
*/
#define MAX_CLIENTS 50
struct client_thread threads[MAX_CLIENTS];
/**
Number of connections we have open right now.
*/
int clientCount = 0;
pthread_rwlock_t message_log_lock = PTHREAD_RWLOCK_INITIALIZER;
#define MAX_MESSAGES 10000
char messageLogRecipients[MAX_MESSAGES];
char *messageLog[MAX_MESSAGES];
int messageCount = 0;
int messageAppend(char *recipient, char *message) {
/*
If used up all message space, exit.
*/
if (messageCount >= MAX_MESSAGES) return -1;
// Append message.
pthread_rwlock_wrlock(&message_log_lock);
messageLogRecipients[messageCount] = strdup(recipient);
message[messageCount] = strdup(message);
messageCount++;
pthread_rwlock_unlock(&message_log_lock);
return 0;
}
int messageRead(struct client_thread *t) {
pthread_rwlock_rdlock(&message_log_lock);
int i;
for (i = t->next_message + 1; i < messageCount; i++) {
}
t -> next_message = messageCount;
pthread_rwlock_unlock(&message_log_lock);
return 0;
}
int create_listen_socket(int port) {
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == -1) return -1;
int on = 1;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof (on)) == -1) {
close(sock);
return -1;
}
if (ioctl(sock, FIONBIO, (char *) &on) == -1) {
close(sock);
return -1;
}
/* Bind it to the next port we want to try. */
struct sockaddr_in address;
bzero((char *) &address, sizeof (address));
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(port);
if (bind(sock, (struct sockaddr *) &address, sizeof (address)) == -1) {
close(sock);
return -1;
}
if (listen(sock, 20) != -1) return sock;
close(sock);
return -1;
}
int accept_incoming(int sock) {
struct sockaddr addr;
unsigned int addr_len = sizeof addr;
int asock;
if ((asock = accept(sock, &addr, &addr_len)) != -1) {
return asock;
}
return -1;
}
int read_from_socket(int sock, unsigned char *buffer, int *count, int buffer_size,
int timeout) {
fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, NULL) | O_NONBLOCK);
int t = time(0) + timeout;
if (*count >= buffer_size) return 0;
int r = read(sock, &buffer[*count], buffer_size - *count);
while (r != 0) {
if (r > 0) {
(*count) += r;
break;
}
r = read(sock, &buffer[*count], buffer_size - *count);
if (r == -1 && errno != EAGAIN) {
perror("read() returned error. Stopping reading from socket.");
return -1;
} else usleep(100000);
// timeout after a few seconds of nothing
if (time(0) >= t) break;
}
buffer[*count] = 0;
return 0;
}
/**
Function to check is a user has registered yet or not.
*/
int registrationCheck(struct client_thread *t) {
if (t->user_has_registered)
return -1;
if (t->user_command_seen && t->nickname[0]) {
// User has met registration requirements.
t->user_has_registered = 1;
t->timeout = 60;
char msg[8192];
snprintf(msg, 1024, ":myircServer.com 001 %s : You have registered.\n", t->nickname);
write(t->sock, msg, strlen(msg));
snprintf(msg, 1024, ":myircServer.com 002 %s : You have registered.\n", t->nickname);
write(t->sock, msg, strlen(msg));
snprintf(msg, 1024, ":myircServer.com 003 %s : You have registered.\n", t->nickname);
write(t->sock, msg, strlen(msg));
snprintf(msg, 1024, ":myircServer.com 004 %s : You have registered.\n", t->nickname);
write(t->sock, msg, strlen(msg));
snprintf(msg, 1024, ":myircServer.com 253 %s : Unknown connections\n", t->nickname);
write(t->sock, msg, strlen(msg));
snprintf(msg, 1024, ":myircServer.com 254 %s : Channels formed\n", t->nickname);
write(t->sock, msg, strlen(msg));
snprintf(msg, 1024, ":myircServer.com 255 %s : I have ??? clients and ??? servers.\n", t->nickname);
write(t->sock, msg, strlen(msg));
return 0;
}
return -1;
}
int connectionMain(struct client_thread *t) {
int sock = t->sock;
char nickname[8192];
char msg[1024];
snprintf(msg, 1024, ":myircserver.com 020 * :Greetings, from the IRC server\n");
write(sock, msg, strlen(msg));
unsigned char buffer[8192];
int length = 0;
t->timeout = 5;
int timeOfLastData = time(0);
while (1) {
length = 0;
messageRead(t);
read_from_socket(sock, buffer, &length, 8192, 1);
if (length > 0)
{
timeOfLastData = time(0);
}
if (length == 0 && ((time(0) - timeOfLastData) >= t->timeout)) {
snprintf(msg, 1024, "ERROR :Closing Link: Connection timed out.. See ya!\n");
write(sock, msg, strlen(msg));
close(sock);
return 0;
}
buffer[length] = 0;
char channel[8192];
int r = sscanf((char *) buffer, "JOIN %s", channel);
if (r == 1) {
if (!t->user_has_registered) {
snprintf(msg, 1024, ":myircserver.com 241 * :JOIN command sent before registration\n");
write(sock, msg, strlen(msg));
}
}
r = sscanf((char *) buffer, "NICK %s", nickname);
if (r == 1) {
/**
check and saw a nickname from the client.
Need to handle what to do next.
Checks if the nickname provided is less than 1 or greater than 30.
if it is, then send and error since it is invalid and we cannot deal with nicknames of these sizes.
if this isn't in place we would be trying to pass a string to big or too small to our nickname variable.
*/
if (strlen(nickname) > 30 || strlen(nickname) < 1) {
snprintf(msg, 1024, "ERROR :Inviliad nickname: Nickname too short or too long.\n");
write(sock, msg, strlen(msg));
} else {
/**
Nickname is a valid length!
copy nickname to thread variable nickname.
*/
strcpy(t->nickname, nickname);
registrationCheck(t);
}
}
/**
Saw USER command if a NICK has been provided. Then mark connection.
As registered. and send client greeting messages.
If this isnt here, then we will not beable to handle USER command or the like.
*/
if (!strncasecmp("USER ", (char *) buffer, 5)) {
if (t->nickname) {
/**
Nickname has been provided and user has been registered.
*/
t->user_command_seen = 1;
registrationCheck(t);
}
}
/**
Checks if the messaged parsed is correct.
Checks nickname, length and the response are correct and send the correct respose code.
Checks of the PRIVMSG has at least 10 bytes of data to be used.
Needed to see if there is data, and if it is sent before registration.
*/
if (!strncasecmp("PRIVMSG", (char *) buffer, 7)) {
if (!t->user_has_registered){
snprintf(msg, 1024, ":myircserver.com 241 * :PRIVMSG command sent before registration\n");
write(sock, msg, strlen(msg));
} else {
// Client is registered, so handle the message.
char recipient[1024];
char message [1024];
if (sscanf((char *) buffer, "PRIVMSG %s :%[^\n]", recipient, message) == 2){
messageAppend(recipient, message);
} else {
// msg is wrongly formed, error.
snprintf(msg, 1024, ":myircserver.com 461 %s :Wrongly formed PRIVMSG command sent.\n", t->nickname);
write(sock, msg, strlen(msg));
}
}
}
/**
* Client left, we must check and close this or we will get a SIGPIPE error that will kill program.
* Send an error statement back to user so they know what is going on.
*/
if (!strncasecmp("QUIT", (char *) buffer, 4)) {
snprintf(msg, 1024, "ERROR :Closing Link: Connection timed out (see ya!)\n");
write(sock, msg, strlen(msg));
close(sock);
return 0;
}}
close(sock);
return 0;
}
void *clientThreadEntry(void *arg) {
struct client_thread *t = arg;
/**
Run the threads connection handling code through threads.
*/
connectionMain(t->sock);
t->state = DEAD;
return NULL;
}
int handleConnection(int sock) {
printf("WE GET HERE\n");
int i;
for (i = 0; i < clientCount; i++) {
if (threads[i].state == DEAD) {
break;
}}
if (i > MAX_CLIENTS) {
close(sock);
return 1;
}
// clear out client structure, set up for threads.
bzero(&threads[i], sizeof (struct client_thread));
// store file descriptor into thread array.
threads[i].sock = sock;
threads[i].state = ALIVE;
threads[i].thread_id = i;
if (pthread_create(&threads[i].thread, NULL, clientThreadEntry, &threads[i]))
{
close(sock);
return 1;
}
if (i == clientCount) clientCount++;
return 0;
}
int main(int argc, char **argv) {
signal(SIGPIPE, SIG_IGN);
if (argc != 2) {
fprintf(stderr, "usage: sample <tcp port>\n");
exit(-1);
}
int master_socket = create_listen_socket(atoi(argv[1]));
fcntl(master_socket, F_SETFL, fcntl(master_socket, F_GETFL, NULL)&(~O_NONBLOCK));
while (1) {
int client_sock = accept_incoming(master_socket);
if (client_sock != -1) {
// Got connection -- do something with it.
handleConnection(client_sock);
} else {usleep(10000);}
}
}
You have a compilation warning. Your function connectionMain takes a client_thread pointer:
int connectionMain(struct client_thread *t)
{
But you are calling it with a sock integer:
void *clientThreadEntry(void *arg) {
struct client_thread *t = arg;
/**
Run the threads connection handling code through threads.
*/
connectionMain(t->sock);
t->state = DEAD;
return NULL;
}
Presumably you want to call it like so:
connectionMain(t);
That would at least fix the compilation warning.