#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/un.h>

char buf[4096];

void init_address(struct sockaddr_in *sa, const char* host, int port) {
    memset((char *) sa, 0, sizeof(*sa));
    sa->sin_family = AF_INET;
    sa->sin_port = htons(port);
    if (inet_aton(host, &sa->sin_addr)==0) {
        perror("inet_aton");
    }
}

#ifndef UNIX_PATH_MAX
#define UNIX_PATH_MAX 108
#endif

void init_address_un(struct sockaddr_un *sa, const char* path) {
    memset((char*) sa, 0, sizeof(*sa));
    sa->sun_family = AF_UNIX;
    strncpy(sa->sun_path, path, UNIX_PATH_MAX);
    sa->sun_path[UNIX_PATH_MAX-1]=0;
    if(sa->sun_path[0]=='@') sa->sun_path[0]=0;
}

int s1;
int s2;
struct sockaddr_in peer1;
struct sockaddr_un peer2;

int udp_socket() {
    int s2;
    struct sockaddr_in si_me;

    if ((s2=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1) {
        perror("socket");
        return -1;
    }

    return s2;
}

int unix_socket() {
    int s2;
    struct sockaddr_in si_me;

    if ((s2=socket(AF_UNIX, SOCK_DGRAM, 0))==-1) {
        perror("AF_UNIX socket");
        return -1;
    }
    
    return s2;
}



int main(int argc, char* argv[])
{
    if (argc<4) {
        fprintf(stderr, "Usage: udp2unix host1 port1 [@]unix_socket_path_send [@]unix_socket_path_recv\n");
        return 1;
    }
    char* host1 = argv[1];
    int port1 = atoi(argv[2]);
    char* path_send = argv[3];
    char* path_recv = argv[4];

    s1 = udp_socket();
    s2_send = unix_socket();
    s2_recv = unix_socket();

    init_address(&peer1, host1, port1);
    init_address_un(&peer2_send, path_send);
    init_address_un(&peer2_recv, path_recv);
    
    if (bind(s1, (struct sockaddr*)(&peer1), sizeof(peer1))==-1) {
        perror("bind");
        return -1;
    }
    
    if (connect(s2, (struct sockaddr*)(&peer2), sizeof(peer2))==-1) {
        perror("connect");
        return -1;
    }    

    fcntl(s1, F_SETFL, O_NONBLOCK);
    fcntl(s2, F_SETFL, O_NONBLOCK);

    fd_set rfds;

    for(;;) {
select_repeat:
        FD_ZERO(&rfds);
        FD_SET(s1, &rfds);
        FD_SET(s2, &rfds);
        int maxfd = (s1>s2)?s1:s2;
        int ret = select(maxfd+1, &rfds, NULL, NULL, NULL);

        if(ret==-1) {
            if(errno==EINTR  || errno==EAGAIN) goto select_repeat;
            perror("select");
            return 2;
        }
        
        if(FD_ISSET(s1, &rfds)) {
            socklen_t slen = sizeof(peer1);
            ret = recv(s1, &buf, sizeof(buf), 0);
            if(ret==-1) {
                perror("recvfrom");
                return 4;
            }
            if(ret) {
                ret = send(s2, &buf, ret, 0);
                if (!ret) {
                    perror("sendto");
                } else {
                    write(1, ">", 1);
                }
            }
        }
        if(FD_ISSET(s2, &rfds)) {
            socklen_t slen = sizeof(peer2);
            ret = recv(s2, &buf, sizeof(buf), 0);
            if(ret==-1) {
                perror("recvfrom");
                return 4;
            }
            if(ret) {
                ret = send(s1, &buf, ret, 0);
                if (!ret) {
                    perror("sendto");
                } else {
                    write(1, "<", 1);
                }
            }

        }
    }    
}
