libite 2.6.1
telnet.c
Go to the documentation of this file.
1/* Poor mans telnet expect in C.
2 *
3 * Copyright (c) 2010-2021 Joachim Wiberg <troglobit@gmail.com>
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
24
25#include <errno.h>
26#include <poll.h>
27#include <unistd.h>
28#include <stdlib.h>
29#include <arpa/inet.h>
30#include <sys/socket.h>
31
32#include "lite.h"
33
34#define EXPECT_DEBUG 0
35#define EXPECT_POLL_TIMEOUT 3000 /* 3 sec timeout waiting for server. */
36
37#if EXPECT_DEBUG
38# define TELL(fmt, args...) fprintf (stderr, fmt "\n", ##args)
39#else
40# define TELL(fmt, args...)
41#endif
42
44struct sdbuf {
45 int sd;
46 char *buf;
47};
48
56sdbuf_t *telnet_open(int addr, short port)
57{
58 struct sockaddr_in *sin;
59 struct sockaddr s;
60 sdbuf_t *ctx;
61 int saved;
62 int retries = 10;
63
64 ctx = (sdbuf_t *)malloc(sizeof(sdbuf_t));
65 if (!ctx) {
66// perror("Failed allocating expect ctxect");
67 return NULL;
68 }
69
70 ctx->buf = (char *)malloc(BUFSIZ);
71 if (!ctx->buf) {
72// perror("Failed allocating expect buffer");
73 free(ctx);
74 return NULL;
75 }
76
77 ctx->sd = socket(PF_INET, SOCK_STREAM, 0);
78 if (-1 == ctx->sd) {
79 saved = errno;
80// perror ("Failed creating socket");
81 free(ctx->buf);
82 free(ctx);
83
84 errno = saved;
85 return NULL;
86 }
87
88 retry:
89 sin = (struct sockaddr_in *)&s;
90 sin->sin_family = AF_INET;
91 sin->sin_port = port;
92 sin->sin_addr.s_addr = addr;
93 if (-1 == connect(ctx->sd, &s, sizeof(s))) {
94 if (ETIMEDOUT == errno && retries-- > 0) {
95 sleep(1);
96 goto retry;
97 }
98
99 saved = errno;
100// perror ("Cannot connect to telnet daemon");
101 telnet_close(ctx);
102 errno = saved;
103
104 return NULL;
105 }
106
107 return ctx;
108}
109
117{
118 free(ctx->buf);
119 close(ctx->sd);
120 free(ctx);
121
122 return 0;
123}
124
125static int wait_substr(sdbuf_t *ctx, char *str)
126{
127 struct pollfd pfd = { ctx->sd, POLLIN, 0 };
128 ssize_t len, tot = 0;
129 int err;
130
131 while (1) {
132 err = poll(&pfd, 1, EXPECT_POLL_TIMEOUT);
133 if (err <= 0) {
134 if (0 == err)
135 errno = ETIMEDOUT;
136
137 if (EINTR == errno)
138 continue;
139
140// perror("No prompt");
141 return 1;
142 }
143
144 len = read(ctx->sd, &ctx->buf[tot], BUFSIZ);
145 if (len <= 0) {
146 errno = ENOMSG;
147 return 1;
148 }
149
150 tot += len;
151 ctx->buf[tot] = 0;
152 TELL("Got line: '%s' is it '%s'?", ctx->buf, str);
153
154 if (strstr(ctx->buf, str))
155 return 0;
156 }
157}
158
174int telnet_expect(sdbuf_t *ctx, char *script[], FILE *output)
175{
176 struct pollfd fd = { ctx->sd, POLLIN, 0 };
177 ssize_t len;
178 int i, first = 1, cont = 1, result = 0;
179
180 for (i = 0; script[i]; i++) {
181 /* when expecting the empty string, just wipe any available input */
182 if (!strlen(script[i])) {
183 result = poll(&fd, 1, 0);
184 if (result > 0)
185 lseek(ctx->sd, 0, SEEK_END);
186 } else {
187 if (wait_substr(ctx, script[i])) {
188 result = ENOMSG;
189 return 1;
190 }
191 }
192
193 i++;
194
195 if (strlen(script[i])) {
196 TELL("Sending line: '%s'", script[i]);
197 if (-1 == write(ctx->sd, script[i], strlen(script[i]))) {
198 errno = ENOSPC;
199 return 1;
200 }
201 }
202 }
203
204 while (cont) {
205 char *ptr = ctx->buf;
206 char *tmp;
207
208 result = poll(&fd, 1, EXPECT_POLL_TIMEOUT);
209 if (result <= 0) {
210 if (0 == result)
211 errno = ETIMEDOUT;
212
213 if (EINTR == errno)
214 continue;
215
216// perror ("No result");
217 result = errno;
218 break;
219 }
220
221 /*
222 * On VERY long outputs we need to send some love back to the
223 * server, otherwise it terminates the connection. --Jocke
224 */
225 if (-1 == write(ctx->sd, "\n", 1))
226 goto retry;
227
228 len = read(ctx->sd, ctx->buf, BUFSIZ);
229 if (len == -1) {
230 retry:
231 if (EINTR == errno)
232 continue;
233
234 result = errno;
235 break;
236 }
237 ctx->buf[len] = 0;
238
239 TELL("Read line: '%s'", ctx->buf);
240
241 /* Skip first line -- only an echo of the last command */
242 if (first) {
243 first = 0;
244 ptr = strchr(ctx->buf, '\n');
245 if (ptr)
246 ptr++;
247 else
248 ptr = ctx->buf;
249 }
250
251 TELL("Comparing '%s' with '%s'", ptr, script[i - 2]);
252
253 /* Skip last line -- only the returning prompt. */
254 tmp = strstr(ptr, script[i - 2]);
255 if (tmp) {
256 TELL("OK, last line. Good bye!");
257 result = 0;
258 cont = 0;
259 *tmp = 0;
260
261 tmp = strrchr(ptr, '\n');
262 if (tmp)
263 *++tmp = 0;
264 }
265
266 if (output && *ptr) {
267 if (EOF == fputs(ptr, output)) {
268 /* Only happens when /tmp is full :( */
269 result = ENOSPC;
270 /* We must complete read-out of descriptor */
271 output = NULL;
272 }
273 }
274 }
275
276 return result;
277}
278
295int telnet_session(int addr, short port, char *script[], FILE *output)
296{
297 sdbuf_t *sdbuf;
298 int result = 0;
299
300 sdbuf = telnet_open(addr, port);
301 if (!sdbuf)
302 return errno;
303
304 if (telnet_expect(sdbuf, script, output))
305 result = errno;
306
307 telnet_close(sdbuf);
308
309 return errno = result;
310}
311
struct sdbuf sdbuf_t
Definition lite.h:72
int telnet_session(int addr, short port, char *script[], FILE *output)
Definition telnet.c:295
int telnet_expect(sdbuf_t *ctx, char *script[], FILE *output)
Definition telnet.c:174
sdbuf_t * telnet_open(int addr, short port)
Definition telnet.c:56
int telnet_close(sdbuf_t *ctx)
Definition telnet.c:116