summaryrefslogblamecommitdiffstats
path: root/tcp.c
blob: a1b0ed4d94675a521ba215e8f6ffbc457adf33b9 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11










                       



                               
                                            
                               





















                                                                                      
                                                                






                                                                                       
                                                                                        
                                          

                                              

                                                        


                                                                                               





































                                                                                       
                                
                   
                                                               









                                                                                                                               
                                               



                                             







                                                                  
                                 
























































































































































                                                                                                                             
#pragma once
#include <arpa/inet.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#include <sys/select.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/types.h>
#include <netdb.h>
#include <string.h>
#include <sys/time.h>
#define ERR_INET_ADDR "0.9.9.0"
#define TCPC_READ_BUF 1024 /* en kilobajt */
#define TCPC_RESOLVE_RETRIES 12
union ip_conv {
	unsigned char c[4];
	struct in_addr in;
};
struct in_addr hostname_to_ip (const char * hostname) {
	struct hostent *he; /* STATIC! */
	union ip_conv ipconverter;
	struct in_addr *error_addr = malloc(sizeof(struct in_addr));
	/* int ret = */ inet_aton(ERR_INET_ADDR, error_addr); /* TODO: chek for err */
	if ( (he = gethostbyname(hostname)) == NULL ) {
		herror("gethostbyname");
		return *error_addr;
	} else {
		memcpy(ipconverter.c, he->h_addr, 4); // fuck yes now it works
		// fprintf(stderr, "debug: %s\n", inet_ntoa(ipconverter.in));
		return ipconverter.in;
	}
}
int spawn_conn (const char * address, const int port) {
	int ret;
	int conn_fd;
	struct sockaddr_in server_addr = { 0 };
	unsigned short int r /* etries */= TCPC_RESOLVE_RETRIES;
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(port);
	ret = inet_pton(AF_INET, address, &server_addr.sin_addr);
	if (ret != 1) {
		if (ret == -1)
			perror("inet_pton");
		fprintf(stderr, "%s is not an IPv4, trying to resolve ...\n", address);
		struct in_addr ret; /* zakaj sem tudi to poimenoval ret!? sicer dela. */
		struct in_addr error_addr;
retry_resolve:
		ret = hostname_to_ip(address);
		inet_aton(ERR_INET_ADDR, &error_addr);
		if (memcmp(&ret, &error_addr, 4) == 0) {
			fprintf(stderr, "failed to resolve-%s.\n", r ? "retrying" : "failing");
			if (r--)
				goto retry_resolve;
			return -1;
		}
		server_addr.sin_addr = ret;
		fprintf(stderr, "resolved to  %s.\n", inet_ntoa(server_addr.sin_addr));
	}
	conn_fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
	if(conn_fd == -1) {
		perror("socket");
		return -1;
	}
	ret = connect(conn_fd, (struct sockaddr*)&server_addr, sizeof(server_addr));
	if (ret == 1) {
		perror("connect");
		return -1;
	}
	return conn_fd;
}

int kill_conn (int conn_fd) {
	int ret = shutdown(conn_fd, SHUT_RDWR); // preprečimo tako read kot write.:wq
	if (ret == -1) {
		perror("shutdown");
		return -1;
	}
	ret = close(conn_fd);
	if (ret == -1) {
		perror("close");
		return -1;
	}
	return 0;
}

int read_until(int conn_fd, FILE * out, unsigned int timeout, const char * ma,
		unsigned long long int max_bytes) {
	int ret = 0;
	unsigned int match = 0;
	struct timeval start, stop;
	gettimeofday(&start, NULL);
	char c[TCPC_READ_BUF+1];
	while (1) {
		ret = read(conn_fd, c, ma ? 1 : TCPC_READ_BUF);
		if (ret == -1) {
			if (errno == EWOULDBLOCK) {
			} else {
				fprintf(stderr, "%s@" __FILE__ ":%d read(): %s%d\n", __func__, __LINE__, strerror(errno), ret);
				return 1;
			}
		} else if (ret == 0) { /* strežnik ni poslal ničesar */
			fprintf(stderr, "%s@" __FILE__ ":%d read(): server closed connection\n", __func__, __LINE__);
			return 0;
		} else {
			fwrite(c, ret, 1, out);
			max_bytes--;
			if (max_bytes <= 0) {
				return 0;
			}
			if (ma != NULL) {
				if (ma[match] == c[0]) {
					match++;
					if (match == strlen(ma)) {
						return 0;
					}
				} else {
					match = 0;
				}
			}
		}
		gettimeofday(&stop, NULL);
		if (stop.tv_sec - start.tv_sec > timeout) {
			fprintf(stderr, "%s@" __FILE__ ":%d E_TIMEOUT %ld-%ld>%u\n", __func__,
					__LINE__, stop.tv_sec, start.tv_sec, timeout);
			return 2;
		}
	}
	return 0;
}

int sync_write(int conn_fd, const char * req, int len, unsigned int timeout) {
	int ret = write(conn_fd, req, len);
	struct timeval start, stop;
	gettimeofday(&start, NULL);
	if (ret == -1) {
		if (errno == EBADF) {
			fprintf(stderr, "tcp.c: sync_write: write EBADF: %s\n", strerror(errno));
			return -1;
		}
		while (errno == EWOULDBLOCK) {
			ret = write(conn_fd, req, len);
			if(ret != -1) {
				return 0;
			}
			gettimeofday(&stop, NULL);
			if (stop.tv_sec - start.tv_sec > timeout) {
				fprintf(stderr, "tcp.c: sync_write: E_TIMEOUT %ld-%ld>%u\n",
						start.tv_sec, stop.tv_sec, timeout);
				return -2;
			}
		}
	}
	return 0;
}


#if __INCLUDE_LEVEL__ == 0
	static volatile int sigint = 0;
	void intHandler(int sig) {
		signal(sig, SIG_IGN);
		if (sig == SIGINT)
			sigint++;
		if (sigint >= 3) // some people smash CtrlC multiple times to force quit!
			exit(130);
		signal(sig, intHandler);
	}
	int main (int argc, char ** argv) {
		if (argc != 1+2) {
			fprintf(stderr, "usage: %s ip.v4.ad.dr port\n", argv[0]);
			return 1;
		}
		signal(SIGINT, intHandler);
		#define ADDRESS_ARG argv[1]
		#define PORT_ARG argv[2]
		int conn_fd = spawn_conn(ADDRESS_ARG, atoi(PORT_ARG));
		if (conn_fd < 0) {
			fprintf(stderr, "error connecting!\n");
			return 2;
		} else {
			fprintf(stderr, "suc. conn with fd %d\n\n", conn_fd);
		}
		int buf = 0;
		#define READ_MAX_SIZE 1024
		char read_buf[READ_MAX_SIZE];
		int i = 0;
		char input;
		fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL) | O_NONBLOCK);
		while (1) {
			if (sigint > 0) {
				// fprintf(stderr, "\n" USERSTRING_EXIT_CONFIRM " (y/N)\n");
				// char gotchar = getchar();
				// if (gotchar == 'y' || gotchar == 'Y' || gotchar == 'd' ||
				// 	gotchar == 'D' || gotchar == 'j' || gotchar == 'J') {
				// 	kill_conn(conn_fd);
				// 	return 0;
				// }
				if (kill_conn(conn_fd) == 0) {
					fprintf(stderr, "\nconnection killed successfully, exiting ...\n");
				} else {
					fprintf(stderr, "\nconnection killing FAILED, exiting ...\n");
				}
				return 0;
			}
			buf = read(conn_fd, read_buf, READ_MAX_SIZE);
			if (buf == -1 && errno != EWOULDBLOCK) {
				fprintf(stderr, "\nerror reading from socket.\n");
				if (kill_conn(conn_fd) != 0) {
					fprintf(stderr, "\nerror killing socket\n");
					return 3;
				}
				return 4;
			}
			if (buf == 0) {
				fprintf(stderr, "\nserver closed socket\n");
				if (kill_conn(conn_fd) != 0) {
					fprintf(stderr, "\nerror killing socket\n");
					return 5;
				}
				return 6;
			}
			if (errno == EWOULDBLOCK && buf == -1) {
				// no data is on socket; sockets are non blocking.
			} else {
				buf = write(STDOUT_FILENO, read_buf, buf);
				if(buf == -1) {
					fprintf(stderr, "\nerror writing to stdout\n");
					return 7;
				}
			}
			buf = read(STDIN_FILENO, read_buf, READ_MAX_SIZE);
			if (buf == -1 && errno != EWOULDBLOCK) {
				fprintf(stderr, "\nerror reading from stdin\n");
				return 8;
			}
			if (buf == 0) {
				// fprintf(stderr, "\neof on stdin\n"); // that's okay
				// return 9;
			}
			if (errno == EWOULDBLOCK && buf == -1) {
				// no data in stdin
			} else {
				i = write(conn_fd, read_buf, buf);
				if (i == -1) {
					if(errno != EWOULDBLOCK) {
						fprintf(stderr, "\nerror writing to socket\n");
						if(kill_conn(conn_fd) != 0) {
							fprintf(stderr, "\nerror killing connection\n");
							return 10;
						}
						return 12;
					} else {
						while (i == -1) {
							if (errno == EWOULDBLOCK) {
								fprintf(stderr, "\nwrite to socket blocked, trying again\n");
								i = write (conn_fd, read_buf, buf);
							} else {
								fprintf(stderr, "\nerror writing to socket\n");
								if (kill_conn(conn_fd) != 0) {
									fprintf(stderr, "\nerror killing connection\n");
									return 13;
								}
								return 14;
							}
						}
					}
				}
			}
			buf = 0;
		}
	}
#endif