Proxy server in C - Multiple - c

I have a proxy server code written in C. The program accepts arguments, eg google.com 9000 80
And then in your browser as you enter localhost: 9000 get google.com page. But I'd like to be able to create several tunnels at once but I do not know how to do it, because in the main function is infinite loop on of which is the basis of the work program.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <netdb.h>
#include <string.h>
#include <signal.h>
#include <assert.h>
#include <syslog.h>
#include <sys/types.h>
#include <sys/select.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/ftp.h>
#include <arpa/inet.h>
#include <arpa/telnet.h>
#define BUF_SIZE 4096
extern int sys_nerr, errno;
char client_hostname[64];
void set_nonblock(int fd)
{
int fl;
int x;
x = fcntl(fd, F_GETFL, &fl);
if (x < 0) {
exit(1);
}
fl |= O_NONBLOCK;
x = fcntl(fd, F_SETFL, &fl);
if (x < 0) {
exit(1);
}
}
int serwer_gniazdo(char *addr, int port)
{
int addrlen, s, on = 1, x;
static struct sockaddr_in client_addr;
s = socket(AF_INET, SOCK_STREAM, 0);
if (s < 0)
perror("socket"), exit(1);
addrlen = sizeof(client_addr);
memset(&client_addr, '\0', addrlen);
client_addr.sin_family = AF_INET;
client_addr.sin_addr.s_addr = inet_addr(addr);
client_addr.sin_port = htons(port);
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, 4);
x = bind(s, (struct sockaddr *) &client_addr, addrlen);
if (x < 0)
perror("bind"), exit(1);
x = listen(s, 5);
if (x < 0)
perror("listen"), exit(1);
return s;
}
int otworz_host(char *host, int port)
{
struct sockaddr_in rem_addr;
int len, s, x;
struct hostent *H;
int on = 1;
H = gethostbyname(host);
if (!H)
return (-2);
len = sizeof(rem_addr);
s = socket(AF_INET, SOCK_STREAM, 0);
if (s < 0)
return s;
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, 4);
len = sizeof(rem_addr);
memset(&rem_addr, '\0', len);
rem_addr.sin_family = AF_INET;
memcpy(&rem_addr.sin_addr, H->h_addr, H->h_length);
rem_addr.sin_port = htons(port);
x = connect(s, (struct sockaddr *) &rem_addr, len);
if (x < 0) {
close(s);
return x;
}
set_nonblock(s);
return s;
}
int sock_addr_info(struct sockaddr_in addr, int len, char *fqdn)
{
struct hostent *hostinfo;
hostinfo = gethostbyaddr((char *) &addr.sin_addr.s_addr, len, AF_INET);
if (!hostinfo) {
sprintf(fqdn, "%s", inet_ntoa(addr.sin_addr));
return 0;
}
if (hostinfo && fqdn)
sprintf(fqdn, "%s [%s]", hostinfo->h_name, inet_ntoa(addr.sin_addr));
return 0;
}
int czekaj_na_polaczenie(int s)
{
int newsock;
static struct sockaddr_in peer;
socklen_t len;
len = sizeof(struct sockaddr);
newsock = accept(s, (struct sockaddr *) &peer, &len);
if (newsock < 0) {
if (errno != EINTR)
perror("accept");
}
sock_addr_info(peer, len, client_hostname);
set_nonblock(newsock);
return (newsock);
}
int zapis(int fd, char *buf, int *len)
{
int x = write(fd, buf, *len);
if (x < 0)
return x;
if (x == 0)
return x;
if (x != *len)
memmove(buf, buf+x, (*len)-x);
*len -= x;
return x;
}
void klient(int cfd, int sfd)
{
int maxfd;
char *sbuf;
char *cbuf;
int x, n;
int cbo = 0;
int sbo = 0;
fd_set R;
sbuf = (char *)malloc(BUF_SIZE);
cbuf = (char *)malloc(BUF_SIZE);
maxfd = cfd > sfd ? cfd : sfd;
maxfd++;
while (1)
{
struct timeval to;
if (cbo)
{
if (zapis(sfd, cbuf, &cbo) < 0 && errno != EWOULDBLOCK) {
exit(1);
}
}
if (sbo) {
if (zapis(cfd, sbuf, &sbo) < 0 && errno != EWOULDBLOCK) {
exit(1);
}
}
FD_ZERO(&R);
if (cbo < BUF_SIZE)
FD_SET(cfd, &R);
if (sbo < BUF_SIZE)
FD_SET(sfd, &R);
to.tv_sec = 0;
to.tv_usec = 1000;
x = select(maxfd+1, &R, 0, 0, &to);
if (x > 0) {
if (FD_ISSET(cfd, &R)) {
n = read(cfd, cbuf+cbo, BUF_SIZE-cbo);
if (n > 0) {
cbo += n;
} else {
close(cfd);
close(sfd);
_exit(0);
}
}
if (FD_ISSET(sfd, &R)) {
n = read(sfd, sbuf+sbo, BUF_SIZE-sbo);
if (n > 0) {
sbo += n;
} else {
close(sfd);
close(cfd);
_exit(0);
}
}
} else if (x < 0 && errno != EINTR) {
close(sfd);
close(cfd);
_exit(0);
}
}
}
int main(int argc, char *argv[])
{
char *localaddr = (char *)"127.0.0.1";
int localport = atoi(argv[1]);
char *remoteaddr = (char *)(argv[2]);
int remoteport = atoi(argv[3]);
int client, server;
int master_sock;
if (4 != argc)
{
fprintf(stderr, "usage: %s port host port\n", argv[0]);
exit(1);
}
assert(localaddr);
assert(localport > 0);
assert(remoteaddr);
assert(remoteport > 0);
master_sock = serwer_gniazdo(localaddr, localport);
for (;;)
{
if ((client = czekaj_na_polaczenie(master_sock)) < 0)
continue;
if ((server = otworz_host(remoteaddr, remoteport)) < 0)
continue;
if (!fork()) {
klient(client, server);
}
close(client);
close(server);
}
printf("Koniec programu");
return 0;
}

The answer is simple: use threads!
Here is a tutorial how to do it:
http://www.binarytides.com/server-client-example-c-sockets-linux/
Some other examples of servers handling multiple connections, if you don't like threads:
http://martinbroadhurst.com/server-examples.html
And if you don't want mess things up (which is always easy in multithreaded code), I recommend reading answer to this question:
Tips to write thread-safe UNIX code?
To make long story short: you need to watch out for any variable that is shared between threads, like globals, statics and arguments passed by pointer. You must avoid situations, when two threads try to write in the same place (for example client_hostname global variable) and then try to read from it, because you may end up with a situation, when you loose one of the values and have two threads from two different clients sharing the same hostname.
Also keep in mind one more thing: three best C programmers I have ever met in person consider multithreading programming as the most difficult part of their job. You are now tackling complicated and complex problem. Don't be discouraged if you failed at first, everyone did at first.
Also, a bit of advice: never mix up two different languages for naming variables. Since you can't get rid of English (because libraries are in English), I advise you to stop using Polish words. Usually it is a standard in most companies to only use English in their source code anyway - even if they are located in non-English speaking country.

Related

How can I program a file transfer system using UDP so that files are transferred instead of messages?

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
int pid;
int sock;
void interrupt(int sig) {
close(sock);
if (pid) {
kill(pid, sig);
printf("Exiting...\n");
}
exit(0);
}
int main(int argc, char *argv[])
{
if (argc != 2) {
printf("Usage: %s portnumber\n", argv[0]);
return 1;
}
signal(SIGINT, &interrupt);
signal(SIGTERM, &interrupt);
int sock = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in addr = {
AF_INET,
htons(atoi(argv[1])),
INADDR_ANY
};
int res = bind( sock,
(struct sockaddr*)&addr,
sizeof(addr));
if (res < 0) {
perror("Bind");
return -1;
}
int pid = fork();
if (pid) {
while(1) {
char buf[100];
int chars = 0;
char c;
int portnumber;
scanf("%d", &portnumber);
struct sockaddr_in target = {
AF_INET,
htons(portnumber),
inet_addr("127.0.01")
};
while(read(0, &c, 1) > 0) {
if(c == '\n') {
// TODO send buf
sendto(sock, buf, chars, 0,
(struct sockaddr*)&target,
sizeof(target));
chars = 0;
break;
} else {
if (chars < 100)
buf[chars++] = c;
else
printf("\n->Maximum character is reached, press enter to send\n");
}
}
}
} else {
struct sockaddr_in incoming;
int lenincoming = sizeof(incoming);
char buf[101];
int chars = 0;
while( (chars = recvfrom(sock, &buf, 100, 0,
(struct sockaddr*)&incoming,
&lenincoming)) >= 0)
{
write(1, buf, chars);
write(1, "\n", 1);
buf[chars] = 0;
if (strcmp(buf, "ok") != 0) {
sendto( sock, "ok", 2, 0,
(struct sockaddr*)&incoming,
lenincoming);
}
}
}
return 0;
}
I want to modify this program so that a file can be sent. It should ask a port number and filename and then send the file in 100 byte packages to the other side. Every package should have starting point of the data. When I get data, I should seek to the location that is specified at the start of the data. Save to fixed filename or if you want transfer filename first. You can assume that you are working on the same system.
I read about "lseek(fd, start, SEEK_SET)". I think it should help. But how to combine it all?

Capital ÅÄÖ not printing correctly

I'm baffled by an issue i'm having when trying to get UTF-8 and swedish characters ÅÄÖ to print correctly in a UNIX-talk clone.
I've set the locale with setLocale() to sv_SE and i'm using wide characters to try to display the characters correctly, lowercase åäö works just fine but somehow the capital variant does not work.
Below is the code in its entirety, i suspect that there is something i'm missing with the character sizes in reader() sender() or putch().
#define _GNU_SOURCE
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <ncurses.h>
#include <pthread.h>
#include <signal.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sched.h>
#include <pthread.h>
#include <locale.h>
#include <wchar.h>
#define MATRIXSIZE 1000
#define STACKSIZE 10000
#define CHATLEN 2048
#include <syslog.h>
int r = 0, i = 0, mode = -1;
wchar_t mybuf[CHATLEN], tmbuf[CHATLEN];
WINDOW *me;
WINDOW *them;
struct stuff {
unsigned int col, row, size, realsize;
pid_t childpid;
pid_t mainpid;
char matrix[MATRIXSIZE];
char nukeline[1024];
int nukesize;
int terminate;
struct massaskit {
int writechan;
int readchan;
int sockfd;
struct sockaddr_in server;
struct sockaddr_in writeclient;
struct sockaddr_in readclient;
int c;
struct hostent *serverhost;
char hostname[256];
uint16_t port;
} bertil;
};
pthread_mutex_t scr_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_t sendthread; // Thread that listens to user's typing, and
// puts the characters on the screen, and
// transmits them over the network.
pthread_t readthread; // Thread that reads characters from the network
// and shows them on the screen.
int srv1(void *ptr)
{
struct massaskit *sockstuff = (struct massaskit *)ptr;
int opt = 1;
if ((sockstuff->sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("socket();");
exit(-1);
}
sockstuff->server.sin_family = AF_INET;
sockstuff->server.sin_addr.s_addr = INADDR_ANY;
sockstuff->server.sin_port = htons(sockstuff->port);
setsockopt(sockstuff->sockfd, SOCK_STREAM, SO_REUSEADDR, &opt,
sizeof(opt));
if (bind
(sockstuff->sockfd, (struct sockaddr *)&sockstuff->server,
sizeof(sockstuff->server)) < 0) {
perror("bind failed");
exit(-1);
}
if ((listen(sockstuff->sockfd, 3)) < 0)
{
perror("listen");
exit(-1);
}
sockstuff->c = sizeof(struct sockaddr_in);
printf("waiting for readchan on %i sockstuff->port..\n",
sockstuff->port);
sockstuff->readchan =
accept(sockstuff->sockfd,
(struct sockaddr *)&sockstuff->readclient,
(socklen_t *) & sockstuff->c);
printf("got a connection! now need a connection on writechan (p:%i)\n",
sockstuff->port);
sockstuff->writechan =
accept(sockstuff->sockfd,
(struct sockaddr *)&sockstuff->writeclient,
(socklen_t *) & sockstuff->c);
printf("got a connection! both read/write (p:%i)\n", sockstuff->port);
shutdown(sockstuff->writechan, SHUT_RD);
shutdown(sockstuff->readchan, SHUT_WR);
if (close(sockstuff->sockfd) != 0)
{
exit(1);
}
return 0;
}
int cli1(void *ptr)
{
/* srv1 starts with readchan, we start with writechan :-) */
struct massaskit *sockstuff = (struct massaskit *)ptr;
int opt = 1;
sockstuff->sockfd = -1; /* make it broken so other function understand */
if ((sockstuff->writechan = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("writechan->socket()");
exit(-1);
}
else
{
printf("socket init is ok.got fd[%i] [writechan]\n",
sockstuff->writechan);
}
setsockopt(sockstuff->writechan, SOCK_STREAM, SO_REUSEADDR, &opt,
sizeof(opt));
if ((sockstuff->serverhost =
gethostbyname(sockstuff->hostname)) == NULL) {
perror("error in resolving hostname :/\n");
exit(-1);
}
else
{
printf("ok, resolved host, now making connection [writechan]!\n");
}
memset(&sockstuff->server, '\0', sizeof(struct in_addr));
sockstuff->server.sin_family = AF_INET;
memcpy(&sockstuff->server.sin_addr.s_addr,
sockstuff->serverhost->h_addr,
(size_t) sockstuff->serverhost->h_length);
sockstuff->server.sin_port = htons(sockstuff->port);
if (connect
(sockstuff->writechan, (struct sockaddr *)&sockstuff->server,
sizeof(sockstuff->server)) == -1) {
perror("connection");
exit(-1);
}
else
{
printf("%s%s", "writechan established,starting readchan and", "sleeping 2s so other end can initalize the readchan.\n");
}
if ((sockstuff->readchan = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("socket();");
exit(-1);
}
else
{
printf("readchan socket init ok.\n");
fflush(stdout);
}
setsockopt(sockstuff->readchan, SOCK_STREAM, SO_REUSEADDR, &opt,
sizeof(opt));
if ((sockstuff->serverhost = gethostbyname(sockstuff->hostname)) == NULL)
{
perror("error in recieve channel, could'nt resolve host bailing.\n");
exit(-1);
}
else
{
printf("readchan could resolve host nice shit alabama\n");
fflush(stdout);
}
memset(&sockstuff->server, '\0', sizeof(struct in_addr));
sockstuff->server.sin_family = AF_INET;
memcpy(&sockstuff->server.sin_addr.s_addr,
sockstuff->serverhost->h_addr,
(size_t) sockstuff->serverhost->h_length);
sockstuff->server.sin_port = htons(sockstuff->port);
if (connect
(sockstuff->readchan, (struct sockaddr *)&sockstuff->server,
sizeof(sockstuff->server)) == -1) {
perror("connect()");
exit(-1);
}
else
{
printf("read chan estabiled. starting program!\n");
fflush(stdout);
}
return 0;
}
void putch(WINDOW * win, wchar_t ch)
{
syslog(LOG_INFO, "%04x", ch);
if (ch == 4 || ch == 7) // Translate left-arrow, backspace to CTL-H
ch = '\b';
if(ch < ' ' && ch != '\t' &&
ch != '\n' && ch != '\b'
)
{
return;
}
pthread_mutex_lock(&scr_mutex); // Get exclusive access to screen.
wechochar(win, ch);
if (ch == '\b')
{
wdelch(win);
refresh();
}
pthread_mutex_unlock(&scr_mutex);
}
void setupscreen()
{
int rows, cols;
initscr();
cbreak();
noecho();
intrflush(stdscr, FALSE);
rows = (LINES - 3) / 2;
cols = COLS - 2;
me = newwin(rows, cols, 1, 1);
them = newwin(rows, cols, rows + 2, 1);
idlok(me, TRUE);
scrollok(me, TRUE);
keypad(me, TRUE);
idlok(them, TRUE);
scrollok(them, TRUE);
border(0, 0, 0, 0, 0, 0, 0, 0);
move(rows + 1, 1);
hline(0, cols);
refresh();
}
void* sender(void *ptr) {
struct stuff *s = (struct stuff *)ptr;
setupscreen();
int ch;
while (1)
{
if (i > CHATLEN - 1)
{
i = 0;
}
ch = wgetch(me);
mybuf[i] = ch;
if (ch == KEY_RESIZE)
{
clear();
endwin();
setupscreen();
wchar_t *p = &mybuf[0];
while (&(*p) < &mybuf[CHATLEN - 1])
{
putch(me, (*p));
p++;
}
p = &tmbuf[0];
while (&(*p) < &tmbuf[CHATLEN - 1])
{
putch(them, (*p));
p++;
}
refresh();
}
else
{
putch(me, mybuf[i]);
int writefd = s->bertil.writechan;
write(writefd, &mybuf[i], sizeof(mybuf[i]));
}
i++;
}
pthread_cancel(sendthread);
return NULL;
}
void* reader(void *ptr) {
struct stuff *s = (struct stuff *)ptr;
int ch;
while(1)
{
if(r> CHATLEN - 1)
{
r = 0;
}
int readfd=s->bertil.readchan;
if((read(readfd,&ch,sizeof(ch))) == 0)
{
endwin();
refresh();
return 0;
}
tmbuf[r] = ch;
putch(them, tmbuf[r]);
r++;
}
pthread_cancel(readthread);
return NULL;
}
int main(int argc, char *argv[])
{
setlocale(LC_ALL, "sv_SE");
memset(mybuf, 0, CHATLEN);
memset(tmbuf, 0, CHATLEN);
struct stuff s;
memset(&s, 0, sizeof(struct stuff));
if (argc == 1)
{
printf("usage %s port [host server on port]\n", argv[0]);
printf("usage %s port host [connecto host:port]\n", argv[0]);
exit(1);
}
else if (argc == 3)
{
s.bertil.port = (uint16_t) atoi(argv[1]);
memset(s.bertil.hostname, 0, 256);
memcpy(s.bertil.hostname, argv[2], strlen(argv[2]));
cli1(&s.bertil);
}
else if (argc == 2)
{
s.bertil.port = (uint16_t) atoi(argv[1]);
srv1(&s.bertil);
}
pthread_create(&readthread, NULL, reader, &s);
pthread_create(&sendthread, NULL, sender, &s);
pthread_join(sendthread, NULL);
pthread_join(readthread,NULL);
}
If you want to try out the program compile it as follows:
gcc file.c -lpthread -lncurses
To serve:
./a.out 1234
To connect:
./a.out 1234 localhost
Any help would be greatly appreciated!
Actually, wchar_t is enough bits, and sign-extension problems do not appear likely.
However, the application using ncurses in two threads (sender and reader), and unless you have compiled it specially and allowed for mutexes, it won't work well. ncurses (like any implementation of curses) uses global variables for maintaining the screen. Multiple threads will exercise the library in unexpected ways.
Further reading:
Official releases (ncurses FAQ):
5.7 (2 November 2008). This provides rudimentary support for threaded applications. It also distributes tack separately.
curs_threads - curses thread support (ncurses manual page)
Try using unsigned char instead of int in your functions.
In putch, you do not need wchar_t, unsigned char (one byte) is fine in UTF-8 (http://www.science.co.il/language/Character-code.asp?s=1252).

error: too many arguments to function ‘strcasecmp’ "Head First C"

I'm trying to get the Internet Knock Knock Protocol Server from O'Reilly's Head First C working but when I try to compile it I receive the following Error.
ikkp_server.c:106:4: error: too many arguments to function ‘strcasecmp’
ikkp_server.c:111:6: error: too many arguments to function ‘strcasecmp’
those lines correspond to the lines,
if(strcasecmp("Who's there?", buf,12))
if(strcasecmp("Oscar who?", buf,10))
The full code is
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
void error(char *msg){
fprintf(stderr, "\n\t\t%s: %s\n\n", msg, strerror(errno));
exit(errno);
}
int catch_signal(int sig, void (*handler)(int)){
struct sigaction action;
action.sa_handler = handler;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
return sigaction(sig, &action, NULL);
}
int read_in(int socket, char *buf, int len){
char *s = buf;
int slen = len;
int c = recv(socket, s, slen, 0);
while ((c > 0) && (s[c-1] != '\n')){
s += c; slen -= c;
c = recv(socket, s, slen, 0);
}
if ( c < 0)
return c;
else if (c == 0)
buf[0] = '\0';
else
s[c -1] = '\0';
return len - slen;
}
int open_listener_socket(){
int s = socket(PF_INET, SOCK_STREAM, 0);
if (s == -1)
error("Open Socket Error");
return s;
}
void bind_to_port(int socket, int port){
struct sockaddr_in name;
name.sin_family = PF_INET;
name.sin_port = (in_port_t)htons(port);
name.sin_addr.s_addr = htonl(INADDR_ANY);
int reuse = 1;
if(setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(int)) == -1)
error("Error reusing socket.");
int c = bind( socket, (struct sockaddr *) &name, sizeof(name));
if (c == -1)
error("Error binding socket.");
}
int say(int socket, char *s){
int result = send(socket, s, strlen(s), 0);
if (result == -1)
error("Can't send message");
return result;
}
int listener_d;
void handle_shutdown(int sig){
if (listener_d)
close(listener_d);
fprintf(stderr, "\n\t\tServer Closed.\n\n");
exit(0);
}
int main(){
//catch sig
if(catch_signal(SIGINT, handle_shutdown) == -1)
error("Signal Error");
//create listener socket..
listener_d = open_listener_socket();
//bind listener.
bind_to_port(listener_d, 3098);
if(listen(listener_d, 10) == -1)
error("Can't Listen.");
struct sockaddr_storage client_addr;
unsigned int address_size = sizeof(client_addr);
puts("Waiting for Connection.");
char buf[255];
while(1){
int connect_d = accept(listener_d, (struct sockaddr *)&client_addr, &address_size);
if( connect_d == -1)
error("Client Connect Error.");
//begin knock knock joke.
if(say(connect_d,"Internet Knock Knock Protocol Server\r\nVersion 1.0\r\n\nKnock! Knock!\r\n" ) != -1){
read_in(connect_d, buf, sizeof(buf));
if(strcasecmp("Who's there?", buf,12))
say(connect_d,"You should say 'Who's there?'\r\n");
else {
if(say(connect_d,"Oscar.") != -1){
read_in(connect_d, buf, sizeof(buf));
if(strcasecmp("Oscar who?", buf,10))
say(connect_d, "You should say 'Oscar who?'\r\n");
else
say(connect_d, "Oscar silly question, get a silly answer!\r\n");
}
}
}
close(connect_d);
}
return 0;
}
man strcasecmp says that it should work?
int strncasecmp(const char *s1, const char *s2, size_t n);
what am I missing?

Sockets behavior differently between BSD (Mac OS X and OpenBSD) and Linux (Ubuntu)

I wrote a man-in-the-middle/proxy server initially on my mac. Essentially the proxy creates a socket and waits for a connection, then connects to another application. This works flawlessly on in OS X and in OpenBSD; however, when porting the proxy to Ubuntu, it doesn't work as intended.
There is two instances of this proxy running, listening on two separate ports. When this is ran on Ubuntu, all the traffic goes through a single port. I also run into a problem when setting the socket to nonblocking (through fcntl) that sometimes it fails with "Invalid Argument"
I am using sys/socket.
Any pitfalls during this port that I am missing?
EDIT:
I believe there are two problems. One being the Invalid Argument, the other being that traffic is being pushed to different ports.
Service 1 binds to Proxy instance 1 which then binds back to the appropriate service on the black box, which kicks off service 2. However, for some reason on Ubuntu it connects to the Instance 1 which is listening on the incorrect port.
EDIT Solution to Invalid Argument for fcntl:
Found out why i was getting the invalid argument, sadly i'm still having the other issue.
fcntl(fd, cmd, arg)
cmd - F_SETFL(long)
I was passing in a pointer to an int instead of the long primitive.
EDIT:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <netdb.h>
#include <string.h>
#include <signal.h>
#include <assert.h>
#include <syslog.h>
#include <sys/types.h>
#include <sys/select.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/ftp.h>
#include <arpa/inet.h>
#include <arpa/telnet.h>
void cleanup(int sig)
{
syslog(LOG_INFO, "Cleaning up...");
exit(0);
}
void sigreap(int sig)
{
int status;
pid_t p;
while ((p = waitpid(-1, &status, WNOHANG)) > 0) {
syslog(LOG_INFO, "sigreap: pid=%d, status=%d\n", (int) p, status);
}
/* doh! */
signal(SIGCHLD, sigreap);
}
void set_nonblock(int fd)
{
long fl;
int x;
fl = fcntl(fd, F_GETFL);
if (fl < 0) {
syslog(LOG_ERR, "fcntl F_GETFL: FD %d: %s", fd, strerror(errno));
exit(1);
}
fl |= O_NONBLOCK;
x = fcntl(fd, F_SETFL, fl);
if (x < 0) {
syslog(LOG_ERR, "fcntl F_SETFL: FD %d: %s", fd, strerror(errno));
exit(1);
}
}
int create_server_sock(char *addr, int port)
{
int addrlen, s, on = 1, x;
static struct sockaddr_in client_addr;
s = socket(AF_INET, SOCK_STREAM, 0);
if (s < 0)
perror("socket"), exit(1);
addrlen = sizeof(client_addr);
memset(&client_addr, '\0', addrlen);
client_addr.sin_family = AF_INET;
client_addr.sin_addr.s_addr = INADDR_ANY; //inet_addr(addr);
client_addr.sin_port = htons(port);
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, 4);
x = bind(s, (struct sockaddr *) &client_addr, addrlen);
if (x < 0)
perror("bind"), exit(1);
x = listen(s, 5);
if (x < 0)
perror("listen"), exit(1);
return s;
}
int open_remote_host(char *host, int port)
{
struct sockaddr_in rem_addr;
int len, s, x;
struct hostent *H;
int on = 1;
H = gethostbyname(host);
if (!H)
return (-2);
len = sizeof(rem_addr);
s = socket(AF_INET, SOCK_STREAM, 0);
if (s < 0)
return s;
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, 4);
len = sizeof(rem_addr);
memset(&rem_addr, '\0', len);
rem_addr.sin_family = AF_INET;
memcpy(&rem_addr.sin_addr, H->h_addr, H->h_length);
rem_addr.sin_port = htons(port);
x = connect(s, (struct sockaddr *) &rem_addr, len);
if (x < 0) {
close(s);
return x;
}
set_nonblock(s);
return s;
}
int get_hinfo_from_sockaddr(struct sockaddr_in addr, int len, char *fqdn)
{
struct hostent *hostinfo;
hostinfo = gethostbyaddr((char *) &addr.sin_addr.s_addr, len, AF_INET);
if (!hostinfo) {
sprintf(fqdn, "%s", inet_ntoa(addr.sin_addr));
return 0;
}
if (hostinfo && fqdn)
sprintf(fqdn, "%s [%s]", hostinfo->h_name, inet_ntoa(addr.sin_addr));
return 0;
}
int wait_for_connection(int s)
{
int newsock;
socklen_t len;
static struct sockaddr_in peer;
len = sizeof(struct sockaddr);
newsock = accept(s, (struct sockaddr *) &peer, &len);
/* dump_sockaddr (peer, len); */
if (newsock < 0) {
if (errno != EINTR)
perror("accept");
}
get_hinfo_from_sockaddr(peer, len, client_hostname);
set_nonblock(newsock);
return (newsock);
}
static int print_bytes(char * buf, ssize_t length)
{
int i = 0, ascii_off = 0, hex_off = 0;
char * hex_bytes = (char *) calloc(32*2,1);
char * ascii_bytes = (char *) calloc(32*2,1);
for( i = 0; i < length; i++)
{
hex_off += sprintf(hex_bytes+hex_off,"%02X ",(unsigned char)buf[i]);
if(buf[i] >= '!' && buf[i] <= '~')
ascii_off += sprintf(ascii_bytes+ascii_off,"%c ",buf[i]);
else
ascii_off += sprintf(ascii_bytes+ascii_off,". ");
if( ((i+1) % 16 == 0) || i == length-1 )
{
fprintf(stderr,"%-48s\t%s\n",hex_bytes,ascii_bytes);
free(hex_bytes);
free(ascii_bytes);
hex_bytes = (char *) calloc(32*2,1);
ascii_bytes = (char *) calloc(32*2,1);
ascii_off = 0;
hex_off = 0;
if(i != length-1)
fprintf(stderr,"\t");
}
}
free(hex_bytes);
free(ascii_bytes);
return 0;
}
int mywrite(int fd, char *buf, int *len)
{
int x = write(fd, buf, *len);
print_bytes(buf,*len);
if (x < 0)
return x;
if (x == 0)
return x;
if (x != *len)
memmove(buf, buf+x, (*len)-x);
*len -= x;
return x;
}
void service_client(int fd1, int fd2, int injfd)
{
int maxfd;
cfd = fd1;
sfd = fd2;
char *sbuf;
char *cbuf;
int x, n;
int cbo = 0;
int sbo = 0;
int ibo = 0;
fd_set R;
int max_clients = 30;
int i = 0,s = 0, addrlen;
struct sockaddr_in address;
sbuf = malloc(BUF_SIZE);
cbuf = malloc(BUF_SIZE);
cntrlbuf = calloc(1,BUF_SIZE);
char * injbuf = malloc(BUF_SIZE);
maxfd = cfd > sfd ? cfd : sfd;
maxfd = injfd > maxfd ? injfd : maxfd;
maxfd++;
maxfd++;
struct inj_con * ptr;
while (1) {
struct timeval to;
if (cbo) {
process_packet(cbuf,&cbo,sfd);
}
if (sbo) {
process_packet(sbuf,&sbo,cfd);
}
if (ibo) {
process_packet(injbuf,&ibo,cfd);
}
if (cntrlo) {
fprintf(stderr,"\033[33;1mControl->(%d), len = 0x%x (%d):\033[0m\n\t",cfd,cntrlo,cntrlo);
if (mywrite(cfd, cntrlbuf, &cntrlo) < 0 && errno != EWOULDBLOCK) {
syslog(LOG_ERR, "write %d: %s", cfd, strerror(errno));
exit(1);
}
}
FD_ZERO(&R);
if (cbo < BUF_SIZE)
FD_SET(cfd, &R);
if (sbo < BUF_SIZE)
FD_SET(sfd, &R);
if (ibo < BUF_SIZE)
FD_SET(injfd, &R);
to.tv_sec = 0;
to.tv_usec = 1000;
x = select(max_clients+3, &R, 0, 0, &to);
if (x > 0 || cntrl_q->item_count > 0) {
if (FD_ISSET(injfd, &R)) {
int new_socket;
if((new_socket = accept(injfd, (struct sockaddr *) &address, (socklen_t *) &addrlen)) < 0)
{
perror("accept");
exit(1);
}
// Truncated
//
}
char * temp_pkt;
if (FD_ISSET(cfd, &R)) {
temp_pkt = (char *) calloc(BUF_SIZE,1);
n = read(cfd, temp_pkt, BUF_SIZE);
syslog(LOG_INFO, "read %d bytes from CLIENT (%d)", n, cfd);
if (n > 0) {
push_msg(s_q,temp_pkt,n);
} else {
free(temp_pkt);
close(cfd);
close(sfd);
close_injection_sockets();
close(injfd);
_exit(0);
}
}
if (FD_ISSET(sfd, &R)) {
temp_pkt = (char *) calloc(BUF_SIZE,1);
n = read(sfd, temp_pkt, BUF_SIZE);
syslog(LOG_INFO, "read %d bytes from SERVER (%d)\n", n, sfd);
if (n > 0) {
push_msg(c_q,temp_pkt,n);
} else {
free(temp_pkt);
close(sfd);
close(cfd);
close_injection_sockets();
close(injfd);
_exit(0);
}
}
if(cntrlo == 0 && cntrl_q->front != NULL)
{
struct msg * tmp = next_msg(cntrl_q);
if(tmp != NULL)
{
memcpy(cntrlbuf,tmp->msg,tmp->len);
cntrlo += tmp->len;
free(tmp->msg);
free(tmp);
}
}
if(sbo == 0 && c_q->front != NULL)
{
struct msg * tmp = next_msg(c_q);
if(tmp != NULL)
{
memcpy(sbuf,tmp->msg,tmp->len);
sbo += tmp->len;
free(tmp->msg);
free(tmp);
}
}
if(cbo == 0 && s_q->front != NULL)
{
struct msg * tmp = next_msg(s_q);
if(tmp != NULL)
{
memcpy(cbuf,tmp->msg,tmp->len);
cbo += tmp->len;
free(tmp->msg);
free(tmp);
}
}
if(ibo == 0 && inj_q->front != NULL)
{
struct msg * tmp = next_msg(inj_q);
if(tmp != NULL)
{
memcpy(injbuf,tmp->msg,tmp->len);
ibo += tmp->len;
free(tmp->msg);
free(tmp);
}
}
} else if (x < 0 && errno != EINTR) {
close(sfd);
close(cfd);
_exit(0);
}
}
}
static int create_injection_sock(int injectionport)
{
struct sockaddr_in serv_addr;
int portno = injectionport;
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd <0)
{
perror("ERROR: opening socket");
exit(1);
}
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
{
perror("ERROR: on bind");
exit(1);
}
if (listen(sockfd,5) < 0 )
{
perror("listen injection");
exit(1);
}
return sockfd;
}
int main(int argc, char *argv[])
{
if (!(5 == argc || 6 == argc)) {
fprintf(stderr, "usage: %s laddr lport rhost rport [injectionport]\n", argv[0]);
exit(1);
}
char *localaddr = strdup(argv[1]);
int localport = atoi(argv[2]);
char *remoteaddr = strdup(argv[3]);
int remoteport = atoi(argv[4]);
int injectionport;
if(argc == 6)
injectionport = atoi(argv[5]);
int client, server;
int master_sock;
int injection_sock = -1;
cntrl_q = (struct item_queue *) calloc(1,sizeof(struct item_queue));
inj_q = (struct item_queue *) calloc(1,sizeof(struct item_queue));
s_q = (struct item_queue *) calloc(1,sizeof(struct item_queue));
c_q = (struct item_queue *) calloc(1,sizeof(struct item_queue));
identities = (struct item_queue *) calloc(1,sizeof(struct item_queue));
assert(localaddr);
assert(localport > 0);
assert(remoteaddr);
assert(remoteport > 0);
if(argc == 6)
assert(injectionport > 0);
openlog(argv[0], LOG_PID, LOG_LOCAL4);
signal(SIGINT, cleanup);
signal(SIGCHLD, sigreap);
if(argc == 6)
injection_sock = create_injection_sock(injectionport);
master_sock = create_server_sock(localaddr, localport);
for (;;) {
if ((client = wait_for_connection(master_sock)) < 0)
continue;
if ((server = open_remote_host(remoteaddr, remoteport)) < 0)
continue;
if (!fork()) {
service_client(client, server, injection_sock);
}
close(client);
close(server);
}
}
Just a guess, but I think your problem is the way you're handling the injection socket. I don't know what you deleted in the // TRUNCATED code block, but basically what you're doing is this:
// Parent process -- bind & listen on the injection socket
injfd = socket(...);
bind(injfd, ...);
listen(injfd, ...);
...
while (1)
{
client = wait_for_connection();
...
if (!fork())
{
// Each child process
...
if (stuff && FD_ISSET(injfd, &R)) {
new_socket = accept(injfd);
...
}
...
}
...
}
In other words, all of your child processes are listening on the same injection socket and trying to accept connections. I'm not sure if this even well-defined behavior, but even in the best case, when a new connection arrives on the injection port, the process that is going to accept the connection could be random and uncontrollable. It could be any of the child processes or even the parent process.
In order to control that behavior, you need to decide who should be listening for connections on the injection socket. If only the parent should be listening, then only the parent should call accept() on it or pass it as an argument to select(). Likewise, if only a particular child should be listening, then only that child should call accept() or pass it to select(). All other processes that are ignoring that socket should close() it at their earliest convenience (e.g. immediately after fork() returns) to avoid leaking file descriptors.
Turns out that SO_REUSEADDR socket option was the issue. I removed me setting that socket option and everything worked out.
This Lead me to the solution.

C - Simple ipv6 udp server using select to listen on multiple ports. Receiving message from one port, not the other

Here is my code.
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/select.h>
#include <stdio.h>
#include <string.h>
int max(int socket_handle[]);
int main( void )
{
int max_clients_allowed = 2;
int socket_handle[max_clients_allowed];
int client_handle[max_clients_allowed];
struct sockaddr_in6 server_address[max_clients_allowed];
struct sockaddr_in6 client_address[max_clients_allowed];
char buffer[1000];
socklen_t client_length;
fd_set read_handles;
struct timeval timeout_interval;
int bytes_received;
int port_number = 9000;
int retval;
int i;
printf("Hello, human.\n");
for (i = 0; i < max_clients_allowed; i++)
{
printf("Creating socket%d on port: %d\n", i, port_number + i);
socket_handle[i] = socket(PF_INET6,SOCK_DGRAM,0);
memset( &server_address[i], 0, sizeof( server_address[i] ) );
server_address[i].sin6_family = AF_INET6;
server_address[i].sin6_addr=in6addr_any;
server_address[i].sin6_port=htons( port_number + i );
if(bind( socket_handle[i], (struct sockaddr *)&server_address[i], sizeof( server_address[i] )) < 0)
{
perror("Unable to bind.");
return -1;
}
else
{
printf("Bind %d successful.\n", i);
}
}
while (1) {
FD_ZERO(&read_handles);
FD_SET(socket_handle[0], &read_handles);
FD_SET(socket_handle[1], &read_handles);
timeout_interval.tv_sec = 2;
timeout_interval.tv_usec = 500000;
retval = select(max(socket_handle) + 1, &read_handles, NULL, NULL, &timeout_interval);
if (retval == -1)
{
printf("Select error\n");
//error
}
else if ((retval = 0))
{
printf("timeout\n");
}
else
{
//good
client_length = sizeof(struct sockaddr*);
for (i = 0; i < max_clients_allowed; i++)
{
if (FD_ISSET(socket_handle[i], &read_handles))
{
if((bytes_received = recvfrom(socket_handle[i],buffer,sizeof(buffer),0,(struct sockaddr *)&client_address[i], (socklen_t*)&client_length)) < 0)
{
perror("Error in recvfrom.");
break;
}
printf("\nData received:");
printf("\n--------------\n");
printf("%s", buffer);
}
}
}
}
}
int max(int socket_handle[])
{
if (socket_handle[0] > socket_handle[1])
{
return socket_handle[0];
}
return socket_handle[1];
}
This code is supposed to bind to port 9000 and 9001. It them uses select to see which sockets has incoming data, and then prints the message.
I'm assuming it has something to do with my recvfrom function. I've tried playing around with the parameters with no avail.
Another problem might be when I am setting up the sockets, I am using sin6.addr = in6addr_any. I'm pretty sure PF_INET6 and AF_INET6 are right, but that could be an issue too. I've been playing with this for a bit but I can't find the bug. I would be really grateful if someone could point out my mistake so I could fix it. I know I'm on the cusp of getting this finished.
This is a homework problem, the teacher gave us the test program and all it does is simply send a message on port 9000.
When calling recvfrom(), you are setting the client_length to the wrong value on input. It needs to be set to sizeof(client_address[I]) instead. You also need to reset the client_length each time you call recvfrom().
When you print the received buffer, you need to take bytes_received into account since the buffer will not be null-terminated (unless the client is sending null-terminated data).
Try this instead:
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/select.h>
#include <stdio.h>
#include <string.h>
int main( void )
{
int max_servers = 2;
int server_handle[max_servers];
int max_server_handle = 0;
struct sockaddr_in6 server_address[max_servers];
struct sockaddr_in6 client_address[max_servers];
char buffer[1000];
socklen_t client_length;
fd_set read_handles;
struct timeval timeout_interval;
int bytes_received;
int port_number = 9000;
int retval;
int i;
printf("Hello, human.\n");
for (i = 0; i < max_servers; i++)
{
printf("Creating socket %d on port: %d\n", i, port_number + i);
server_handle[i] = socket(PF_INET6, SOCK_DGRAM, 0);
if (server_handle[i] < 0)
{
perror("Unable to create socket.");
return -1;
}
if (server_handle[i] > max_server_handle)
max_server_handle = server_handle[i];
memset( &server_address[i], 0, sizeof( server_address[i] ) );
server_address[i].sin6_family = AF_INET6;
server_address[i].sin6_addr = in6addr_any;
server_address[i].sin6_port = htons( port_number + i );
if (bind( server_handle[i], (struct sockaddr *)&server_address[i], sizeof( server_address[i] )) < 0)
{
perror("Unable to bind.");
return -1;
}
printf("Bind %d successful.\n", i);
}
while (1)
{
FD_ZERO(&read_handles);
for (i = 0; i < max_servers; i++)
FD_SET(server_handle[i], &read_handles);
timeout_interval.tv_sec = 2;
timeout_interval.tv_usec = 500000;
retval = select(max_server_handle + 1, &read_handles, NULL, NULL, &timeout_interval);
if (retval == -1)
{
printf("Select error\n");
//error
}
else if (retval == 0)
{
printf("timeout\n");
}
else
{
//good
for (i = 0; i < max_servers; i++)
{
if (FD_ISSET(server_handle[i], &read_handles))
{
client_length = sizeof(client_address[i]);
if ((bytes_received = recvfrom(server_handle[i], buffer, sizeof(buffer), 0, (struct sockaddr *)&client_address[i], &client_length)) < 0)
{
perror("Error in recvfrom.");
break;
}
printf("\nData received on socket %d:", i);
printf("\n--------------\n");
printf("%.*s", bytes_received, buffer);
}
}
}
}
}

Resources