I am using libtelnet to design a telnet client by sending text commands to a telnet server and receiving text responses. I am utilizing telnet-client.c. For simplicity, I made send and receive functions. The complete code is shown below:
#include <arpa/inet.h>
#include <ctype.h>
#include <errno.h>
#include <netdb.h>
#include <netinet/in.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <termios.h>
#include <unistd.h>
#ifdef HAVE_ZLIB
#include "zlib.h"
#endif
#include "libtelnet.h"
#define BUFFER_SIZE 512
static telnet_t *telnet;
static const telnet_telopt_t telopts[] = {
{TELNET_TELOPT_ECHO, TELNET_WONT, TELNET_DO},
{TELNET_TELOPT_TTYPE, TELNET_WILL, TELNET_DONT},
{TELNET_TELOPT_COMPRESS2, TELNET_WONT, TELNET_DO},
{TELNET_TELOPT_MSSP, TELNET_WONT, TELNET_DO},
{-1, 0, 0}};
static void _input(const char *buffer, int size) {
/* printing here only for debugging */
char msg[size + 1];
strncpy(msg, buffer, size);
msg[size] = '\0';
printf("request: [%s]", msg);
static char crlf[] = {'\r', '\n'};
int i;
for (i = 0; i != size; ++i) {
if (buffer[i] == '\r' || buffer[i] == '\n') {
telnet_send(telnet, crlf, 2);
} else {
telnet_send(telnet, buffer + i, 1);
}
}
fflush(stdout);
}
static void _send(int sock, const char *buffer, size_t size) {
int rv;
/* send data */
while (size > 0) {
if ((rv = send(sock, buffer, size, 0)) == -1) {
fprintf(stderr, "send() failed: %s\n", strerror(errno));
exit(1);
} else if (rv == 0) {
fprintf(stderr, "send() unexpectedly returned 0\n");
exit(1);
}
/* update pointer and size to see if we've got more to send */
buffer += rv;
size -= rv;
}
}
static void _event_handler(telnet_t *telnet, telnet_event_t *ev,
void *user_data) {
(void)telnet;
int sock = *(int *)user_data;
char msg[ev->data.size + 1];
switch (ev->type) {
/* data received */
case TELNET_EV_DATA:
strncpy(msg, ev->data.buffer, ev->data.size);
msg[ev->data.size] = '\0';
/* printing here only for debugging */
printf("response: [%s]", msg);
break;
/* data must be sent */
case TELNET_EV_SEND:
_send(sock, ev->data.buffer, ev->data.size);
break;
default:
/* ignore */
break;
}
}
static void try_send(int sock, const char *cmd, size_t cmdlen) {
fd_set writefd;
// timeout 1 second
struct timeval tv;
tv.tv_sec = 1;
tv.tv_usec = 0;
// clear the set ahead of time
FD_ZERO(&writefd);
// add our descriptors to the set
FD_SET(sock, &writefd);
int rv = select(sock + 1, NULL, &writefd, NULL, &tv);
if (rv == -1) {
perror("select"); // error occurred in select()
} else if (rv == 0) {
printf("timeout occurred!\n");
} else {
/* send the request */
if (FD_ISSET(sock, &writefd)) {
_input(cmd, cmdlen);
}
}
}
static void try_recv(int sock, char *buffer) {
fd_set readfd;
struct timeval tv;
tv.tv_sec = 1;
tv.tv_usec = 0;
FD_ZERO(&readfd);
FD_SET(sock, &readfd);
int rv = select(sock + 1, &readfd, NULL, NULL, &tv);
if (rv == -1) {
perror("select"); // error occurred in select()
} else if (rv == 0) {
printf("timeout occurred!\n");
} else {
int rv;
/* receive the response */
if (FD_ISSET(sock, &readfd)) {
if ((rv = recv(sock, buffer, BUFFER_SIZE, 0)) > 0) {
telnet_recv(telnet, buffer, rv);
} else if (rv == 0) {
printf("connection has been closed.\n");
return;
} else {
fprintf(stderr, "recv() failed: %s\n", strerror(errno));
exit(1);
}
}
}
}
int main() {
char buffer[BUFFER_SIZE];
int rv;
int sock;
struct sockaddr_in addr;
struct addrinfo *ai;
struct addrinfo hints;
const char *servname = "50000";
const char *hostname = "192.168.102.85";
/* look up server host */
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if ((rv = getaddrinfo(hostname, servname, &hints, &ai)) != 0) {
fprintf(stderr, "getaddrinfo() failed for %s: %s\n", hostname,
gai_strerror(rv));
return 1;
}
/* create server socket */
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
fprintf(stderr, "socket() failed: %s\n", strerror(errno));
return 1;
}
/* bind server socket */
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
fprintf(stderr, "bind() failed: %s\n", strerror(errno));
close(sock);
return 1;
}
/* connect */
if (connect(sock, ai->ai_addr, ai->ai_addrlen) == -1) {
fprintf(stderr, "connect() failed: %s\n", strerror(errno));
close(sock);
return 1;
}
/* free address lookup info */
freeaddrinfo(ai);
/* initialize telnet box */
telnet = telnet_init(telopts, _event_handler, 0, &sock);
/* our server returns "welcome\r\n" upon a successful connection */
try_recv(sock, buffer);
try_recv(sock, buffer); /* the second recv is needed */
/* define our commands and length */
const char *cmd[] = {"INIT\n", "READ PT3\n", "READ PT4\n", "REM PT3\n"};
const int len[] = {5, 9, 9, 8};
int i;
i = 0;
try_send(sock, cmd[i], len[i]);
try_recv(sock, buffer);
i = 1;
try_send(sock, cmd[i], len[i]);
try_recv(sock, buffer);
i = 2;
try_send(sock, cmd[i], len[i]);
try_recv(sock, buffer);
i = 3;
try_send(sock, cmd[i], len[i]);
try_recv(sock, buffer);
/* clean up */
telnet_free(telnet);
close(sock);
return 0;
}
Run
The server returns "welcome\r\n" upon a successful connection. Please see below the complete log displayed on the terminal.
ravi@dell:~/telnet$ ./my_client
response: [Welcome
]response: [
]request: [INIT
]response: [OK
]request: [READ PT3
]response: [OK
]request: [READ PT4
]response: [OK
]request: [REM PT3
]response: [OK
]ravi@dell:~/telnet$
Problems
I plan to make a wrapper of this code which will later be called from another (main) file. Most of my issues are related to how the response is received. Nevertheless, I feel that the design/code style should be improved. To this end, below are the problems I am facing:
- The call to receive function (i.e.,
try_recv(sock, buffer)
) is done twice in order to grab the welcome message. However, I expect the receive function to receive all the data in one call. Calling the receive function twice does not look nice to me. - Removing the second call or adding an extra receive call makes the program stop working next time. From next time, the program prints "connection has been closed.". In this situation, I have to restart the telnet server forcefully.
- After every send command, the receive function is called. If I do not call receive function, the program prints "connection has been closed.". In this situation, I have to restart the telnet server forcefully.
- The receive function prints the response on the terminal. Instead, I would like to return it as a string.