[dev] [ii] a patch and a program

From: Markus Wichmann <nullplan_AT_gmx.net>
Date: Thu, 11 Jul 2013 22:10:39 +0200

Hi all,

linking ii against uClibc gives a warning about it using
gethostbyname(). I have modified it to use getaddrinfo():


diff --git a/ii.c b/ii.c
index d93266c..117dcf5 100644
--- a/ii.c
+++ b/ii.c
_AT_@ -8,6 +8,7 @@
 #include <sys/socket.h>
 #include <sys/select.h>
 #include <netinet/in.h>
+#include <netdb.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <limits.h>
_AT_@ -23,7 +24,7 @@
 #define PIPE_BUF 4096
 #endif
 #define PING_TIMEOUT 300
-#define SERVER_PORT 6667
+#define SERVER_PORT "6667"
 enum { TOK_NICKSRV = 0, TOK_USER, TOK_CMD, TOK_CHAN, TOK_ARG, TOK_TEXT, TOK_LAST };
 
 typedef struct Channel Channel;
_AT_@ -151,27 +152,32 @@ static void login(char *key, char *fullname) {
         write(irc, message, strlen(message)); /* login */
 }
 
-static int tcpopen(unsigned short port) {
- int fd;
- struct sockaddr_in sin;
- struct hostent *hp = gethostbyname(host);
-
- memset(&sin, 0, sizeof(struct sockaddr_in));
- if(!hp) {
- perror("ii: cannot retrieve host information");
- exit(EXIT_FAILURE);
- }
- sin.sin_family = AF_INET;
- memcpy(&sin.sin_addr, hp->h_addr, hp->h_length);
- sin.sin_port = htons(port);
- if((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
- perror("ii: cannot create socket");
- exit(EXIT_FAILURE);
- }
- if(connect(fd, (const struct sockaddr *) &sin, sizeof(sin)) < 0) {
- perror("ii: cannot connect to host");
+static int tcpopen(const char* port) {
+ int fd, err;
+ struct addrinfo hints = {
+ .ai_family = AF_UNSPEC,
+ .ai_socktype = SOCK_STREAM,
+ .ai_protocol = 0,
+ .ai_flags = 0,
+ }, *res, *it;
+
+ err = getaddrinfo(host, port, &hints, &res);
+ if(err < 0) {
+ fprintf(stderr, "getaddrinfo failed: %s\n", gai_strerror(err));
                 exit(EXIT_FAILURE);
         }
+ for (it = res; it; it = it->ai_next) {
+ if((fd = socket(it->ai_family, it->ai_socktype, it->ai_protocol)) < 0) {
+ perror("ii: cannot create socket");
+ } else if(connect(fd, it->ai_addr, it->ai_addrlen)) {
+ perror("ii: cannot connect to host");
+ close(fd);
+ fd = -1;
+ } else break;
+
+ }
+ freeaddrinfo(res);
+ if (fd == -1) exit(EXIT_FAILURE);
         return fd;
 }
 
_AT_@ -455,7 +461,7 @@ static void run() {
 
 int main(int argc, char *argv[]) {
         int i;
- unsigned short port = SERVER_PORT;
+ const char* port = SERVER_PORT;
         struct passwd *spw = getpwuid(getuid());
         char *key = NULL, *fullname = NULL;
         char prefix[_POSIX_PATH_MAX];
_AT_@ -472,7 +478,7 @@ int main(int argc, char *argv[]) {
                 switch (argv[i][1]) {
                         case 'i': snprintf(prefix,sizeof(prefix),"%s", argv[++i]); break;
                         case 's': host = argv[++i]; break;
- case 'p': port = strtol(argv[++i], NULL, 10); break;
+ case 'p': port = argv[++i]; break;
                         case 'n': snprintf(nick,sizeof(nick),"%s", argv[++i]); break;
                         case 'k': key = getenv(argv[++i]); break;
                         case 'f': fullname = argv[++i]; break;

The advantage of this approach: If hostname resolution returns multiple
addresses and the first one doesn't work, all of them will be tried
until one works. And IPv6 is handled transparently. (BTW: Shouldn't your
code have used the family from the hostent instead of hardcoding
AF_INET?) And ports can be named symbolically.

In any case, I also wrote a program to do DCC file transfers. It's
probably horrible from a technical perspective, since there's next to no
error checking, but hey, at least it's free! Or so. Consider it gifted
to the public domain, though I don't think that sufficient intellectual
work went into it to even merit copyright.


#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char* argv[]) {
    if (argc < 4) return 1;
    int sock;
    FILE* out;
    struct sockaddr_in sin;
    size_t total = 0;
    out = fopen(argv[1], "wb");
    if (!out) return 2;
    sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0) return 3;
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = htonl(atoi(argv[2]));
    sin.sin_port = htons(atoi(argv[3]));

    if (connect(sock, (struct sockaddr*)&sin, sizeof sin) < 0) return 4;
    while (1) {
        char buf[1500];
        ssize_t rv;
        rv = recv(sock, buf, sizeof buf, 0);
        if (rv <= 0) {
            fclose(out);
            close(sock);
            return (rv == 0)?0:5;
        }
        fwrite(buf, 1, rv, out);
        total += rv;
        *(uint32_t*)buf = htonl(total);
        send(sock, buf, 4, 0);
    }
}

The name of the above program is dcc.

Basically, what you do is, when someone offers you a file and you intend
to accept, that someone will send you a message of the form

DCC SEND <filename> <ip address> <port> <totalsize>

You just run this program like

dcc outfilename <ip address> <port>

with IP address and port copied from the file offer (Both will just be
single integers). The file name can be, too, if you want that. In any
case, this tool will then run and output all the gathered data to the
file name mentioned.

As you can see, the DCC file transfer protocol is not particularly hard
on the client side, but I couldn't think of a shell script to do the
job, so I wrote a tool for that. Basically, I didn't know how to send
the current size of the file as a 32-bit big endian binary number.

Hope these help someone,
Markus
Received on Thu Jul 11 2013 - 22:10:39 CEST

This archive was generated by hypermail 2.3.0 : Thu Jul 11 2013 - 22:12:05 CEST