Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Proxy server in C - Multiple

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;
    }
like image 263
lukassz Avatar asked Jan 14 '14 22:01

lukassz


People also ask

What is proxy in C?

Proxy in C++ Proxy is a structural design pattern that provides an object that acts as a substitute for a real service object used by a client. A proxy receives client requests, does some work (access control, caching, etc.) and then passes the request to a service object.

What is proxied request?

When you send a web request, your request goes to the proxy server first. The proxy server then makes your web request on your behalf, collects the response from the web server, and forwards you the web page data so you can see the page in your browser.

How do proxy servers work?

Proxy servers work by facilitating web requests and responses between a user and web server. Typically, a user accesses a website by sending a direct request to its web server from a web browser via their IP address. The web server then sends a response containing the website data directly back to the user.

What is proxy in distributed system?

In computer networking, a proxy server is a server application that acts as an intermediary between a client requesting a resource and the server providing that resource.


1 Answers

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.

like image 142
Darth Hunterix Avatar answered Oct 17 '22 01:10

Darth Hunterix