After studying and considering the reviews for this code, I began working on the code, and came up with this. It has much better error and disconnect detection, and it also resolves addresses:
#include <arpa/inet.h>
#include <error.h>
#include <fcntl.h>
#include <netdb.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#define BUFLEN 1024
sig_atomic_t run = 1;
sig_atomic_t sd = 1;
char e_socket_msg[] = "socket creation failed\n";
char e_sockopt_msg[] = "set socket nonblock failed\n";
char e_parse_msg[] = "address parsing failed\n";
char e_timeout_msg[] = "connection attempt timed out\n";
char e_io_msg[] = "i/o error\n";
char e_generic_msg[] = "unknown or unexpected error\n";
char e_resolve_msg[] = "unable to resolve address\n";
typedef enum {
e_resolve = -1,
e_socket = -2,
e_sockopt = -3,
e_parse = -4,
e_timeout = -5,
e_io = -6
} Error;
void input(char *input, char *output, int len);
int resolve(char *host);
void sig_handler(int sig);
int connect_to(char *host, int port);
int transfer(int fd_in, char *buf, int buf_len, int fd_out);
int print_error(Error e);
int main(void) {
char ch;
fd_set fds;
struct timeval tv;
int rv;
char buffer[BUFLEN];
char host[64], port[16];
char host_msg[] = "host:\t";
char port_msg[] = "port:\t";
input(host_msg, host, 64);
input(port_msg, port, 16);
sd = connect_to(host, atoi(port));
if (sd < 0) {
rv = resolve(host);
if (rv < 0) return print_error(rv);
sd = connect_to(host, atoi(port));
if (sd < 0) return print_error(sd);
}
signal(SIGINT, sig_handler);
signal(SIGPIPE, sig_handler);
FD_ZERO(&fds);
tv.tv_sec = 0;
tv.tv_usec = 300000;
while (run) {
FD_SET(sd, &fds);
FD_SET(STDIN_FILENO, &fds);
rv = select(sd + 1, &fds, NULL, NULL, &tv);
if (FD_ISSET(STDIN_FILENO, &fds))
rv = transfer(STDIN_FILENO, buffer, BUFLEN, sd);
if (FD_ISSET(sd, &fds))
rv = transfer(sd, buffer, BUFLEN, STDOUT_FILENO);
if (rv != 0) {
run = 0;
if (rv > 0) print_error(e_io);
}
}
close(sd);
return 0;
}
void input(char *input, char *output, int len) {
int rv;
(void) write(STDOUT_FILENO, input, strlen(input));
rv = read(STDIN_FILENO, output, len - 1);
output[rv - 1] = '\0';
}
void sig_handler(int sig) {
run = 0;
close(sd);
}
int resolve(char *host) {
struct addrinfo hints, *servinfo;
struct in_addr addr;
char *addr_tmp;
int rv;
memset(&hints, 0, sizeof hints);
hints.ai_socktype = SOCK_STREAM;
hints.ai_family = AF_INET;
rv = getaddrinfo(host, NULL, &hints, &servinfo);
if (rv) return print_error(e_resolve);
addr.s_addr = ((struct sockaddr_in*)servinfo->ai_addr)->sin_addr.s_addr;
addr_tmp = inet_ntoa(addr);
memcpy(host, addr_tmp, strlen(addr_tmp));
freeaddrinfo(servinfo);
}
int connect_to(char *host, int port) {
int sd;
struct sockaddr_in addr;
fd_set sfds;
struct timeval tv;
sd = socket(AF_INET, SOCK_STREAM, 0);
if (sd == -1) return e_socket;
if (fcntl(sd, F_SETFL, O_NONBLOCK) == -1) return e_sockopt;
memset(&addr, 0, sizeof (addr));
addr.sin_family = AF_INET;
if (inet_pton(AF_INET, host, &addr.sin_addr) != 1)
return e_parse;
addr.sin_port = htons(port);
connect(sd, (struct sockaddr *) &addr, sizeof (addr));
FD_ZERO(&sfds);
FD_SET(sd, &sfds);
tv.tv_sec = 4;
tv.tv_usec = 0;
if (select(sd + 1, NULL, &sfds, NULL, &tv)) return sd;
return e_timeout;
}
int transfer(int fd_in, char *buf, int buf_len, int fd_out) {
int len = read(fd_in, buf, buf_len);
return len ? len - write(fd_out, buf, len) : -1;
}
int print_error(Error e) {
char *msg;
switch (e) {
case e_socket:
msg = e_socket_msg;
break;
case e_sockopt:
msg = e_sockopt_msg;
break;
case e_parse:
msg = e_parse_msg;
break;
case e_timeout:
msg = e_timeout_msg;
break;
case e_io:
msg = e_io_msg;
break;
case e_resolve:
msg = e_resolve_msg;
break;
default:
msg = e_generic_msg;
break;
}
(void) write(STDERR_FILENO, msg, strlen(msg));
return -e;
}
I know it's more of a read than my last post, but it should be pretty straight forward. It creates an interactive TCP connection. I modified the code a bit to be usable with "c4droid" through the Android software store. I should note with some rooted phones, this program may possibly not behave properly; as some implementations of root access with Android employ a workaround involving constantly flushing standard streams.