API
 
Loading...
Searching...
No Matches
telnetConn.cpp
Go to the documentation of this file.
1/** \file telnetConn.cpp
2 * \brief Managing a connection to a telnet device.
3 * \author Jared R. Males (jaredmales@gmail.com)
4 *
5 * \ingroup tty_files
6 */
7
8
9#include <cstring>
10
11#include <sys/types.h>
12#include <sys/socket.h>
13#include <netdb.h>
14#include <netinet/in.h>
15
16#include "telnetConn.hpp"
17
18
19namespace MagAOX
20{
21namespace tty
22{
23
25{
26 /* clean up */
27 if(m_telnet) telnet_free(m_telnet);
28 if(m_sock) close(m_sock);
29}
30
31int telnetConn::connect( const std::string & host,
32 const std::string & port
33 )
34{
35 //First cleanup any previous connections.
36 if(m_telnet) telnet_free(m_telnet);
37 m_telnet = 0;
38 if(m_sock) close(m_sock);
39 m_sock = 0;
40
41 m_loggedin = 0;
42
43 struct sockaddr_in addr;
44 struct addrinfo *ai;
45 struct addrinfo hints;
46
47 /* look up server host */
48 memset(&hints, 0, sizeof(hints));
49 hints.ai_family = AF_UNSPEC;
50 hints.ai_socktype = SOCK_STREAM;
51 if( getaddrinfo(host.c_str(), port.c_str(), &hints, &ai) != 0)
52 {
53 return TELNET_E_GETADDR;
54 }
55
56 /* create server m_socket */
57 if ((m_sock = socket(AF_INET, SOCK_STREAM, 0)) == -1)
58 {
59 return TELNET_E_SOCKET;
60 }
61
62 /* bind server socket */
63 memset(&addr, 0, sizeof(addr));
64 addr.sin_family = AF_INET;
65 if (bind(m_sock, (struct sockaddr *)&addr, sizeof(addr)) == -1)
66 {
67 return TELNET_E_BIND;
68 }
69
70 /* connect */
71 if (::connect(m_sock, ai->ai_addr, ai->ai_addrlen) == -1)
72 {
73 return TELNET_E_CONNECT;
74 }
75
76 /* free address lookup info */
77 freeaddrinfo(ai);
78
79 /* initialize the telnet box */
80 m_telnet = telnet_init(telopts, telnetConn::event_handler, 0, this);
81
82 if(m_telnet == nullptr)
83 {
85 }
86
87 return TTY_E_NOERROR;
88}
89
90int telnetConn::login( const std::string & username,
91 const std::string & password
92 )
93{
94 char buffer[1024];
95 int rs;
96
97 struct pollfd pfd[1];
98
99 /* initialize poll descriptors */
100 memset(pfd, 0, sizeof(pfd));
101 pfd[0].fd = m_sock;
102 pfd[0].events = POLLIN;
103
104 //Loop while waiting on the login process to complete.
105 #ifdef TELNET_DEBUG
106 std::cerr << "Starting poll\n";
107 #endif
108 int pollrv;
109 while ( (pollrv = poll(pfd, 1, 30000)) > 0)
110 {
111 #ifdef TELNET_DEBUG
112 std::cerr << "Polled\n";
113 #endif
114 /* read from client */
115 if (pfd[0].revents & POLLIN)
116 {
117 #ifdef TELNET_DEBUG
118 std::cerr << "Starting read\n";
119 #endif
120 if ((rs = recv(m_sock, buffer, sizeof(buffer), 0)) > 0)
121 {
122 #ifdef TELNET_DEBUG
123 std::cerr << "read: " << rs << "bytes\n";
124 #endif
125 telnet_recv(m_telnet, buffer, rs);
126 if(m_EHError != TTY_E_NOERROR) return m_EHError;
127 }
128 else if (rs == 0)
129 {
130 break;
131 }
132 else
133 {
134 fprintf(stderr, "recv(client) failed: %s\n",
135 strerror(errno));
136 return TTY_E_ERRORONREAD;
137 }
138 }
139
141 {
142 int rv = write(username + "\n", 1000);
143 if(rv != TTY_E_NOERROR) return rv;
144
146 }
147
149 {
150 int rv = write(password + "\n", 1000);
151 if(rv != TTY_E_NOERROR) return rv;
152
154 }
155
157 {
158 break;
159 }
160 #ifdef TELNET_DEBUG
161 std::cerr << "polling\n";
162 #endif
163 }
164
165 if(pollrv == 0)
166 {
167 #ifdef TELNET_DEBUG
168 std::cerr << "login timed out\n";
169 #endif
171 }
172
173 return TTY_E_NOERROR;
174}
175
181
182int telnetConn::write( const std::string & buffWrite,
183 int timeoutWrite
184 )
185{
186 double t0;
187 struct pollfd pfd;
188
189 errno = 0;
190 pfd.fd = m_sock;
191 pfd.events = POLLOUT;
192
193 std::string _buffWrite;
194 telnetCRLF(_buffWrite, buffWrite);
195
196
197 t0 = mx::sys::get_curr_time();
198
199 size_t totWritten = 0;
200 while( totWritten < _buffWrite.size())
201 {
202 int timeoutCurrent = timeoutWrite - (mx::sys::get_curr_time()-t0)*1000;
203 if(timeoutCurrent < 0) return TTY_E_TIMEOUTONWRITE;
204
205 int rv = poll( &pfd, 1, timeoutCurrent);
206 if( rv == 0 ) return TTY_E_TIMEOUTONWRITEPOLL;
207 else if( rv < 0 ) return TTY_E_ERRORONWRITEPOLL;
208
209 telnet_send(m_telnet, _buffWrite.c_str(), _buffWrite.size());
210 totWritten = _buffWrite.size();
211
212 #ifdef TELNET_DEBUG
213 std::cerr << "Wrote " << totWritten << " chars of " << buffWrite.size() << "\n";
214 #endif
215
216
217 if( ( mx::sys::get_curr_time()-t0)*1000 > timeoutWrite ) return TTY_E_TIMEOUTONWRITE;
218 }
219
220 return TTY_E_NOERROR;
221}
222
223int telnetConn::read( const std::string & eot,
224 int timeoutRead,
225 bool clear
226 )
227{
228 int rv;
229 int timeoutCurrent;
230 double t0;
231
232 struct pollfd pfd;
233
234 errno = 0;
235
236 pfd.fd = m_sock;
237 pfd.events = POLLIN;
238
239 char buffRead[TELNET_BUFFSIZE];
240
241 //Start timeout clock for reading.
242 t0 = mx::sys::get_curr_time();
243 timeoutCurrent = timeoutRead;
244
245 //Now read the response up to the eot.
246 if(clear) m_strRead = "";
247
248
249 rv = poll( &pfd, 1, timeoutCurrent);
250 if( rv == 0 ) return TTY_E_TIMEOUTONREADPOLL;
251 if( rv < 0 ) return TTY_E_ERRORONREADPOLL;
252
253 rv = ::read(m_sock, buffRead, TELNET_BUFFSIZE);
254 if( rv < 0 ) return TTY_E_ERRORONREAD;
255 buffRead[rv] = '\0';
256
257 telnet_recv(m_telnet, buffRead, rv);
258 if(m_EHError != TTY_E_NOERROR) return m_EHError;
259
260 while( !isEndOfTrans(m_strRead, eot) )
261 {
262 timeoutCurrent = timeoutRead - (mx::sys::get_curr_time()-t0)*1000;
263 if(timeoutCurrent < 0) return TTY_E_TIMEOUTONREAD;
264
265 rv = poll( &pfd, 1, timeoutCurrent);
266 if( rv == 0 ) return TTY_E_TIMEOUTONREADPOLL;
267 if( rv < 0 ) return TTY_E_ERRORONREADPOLL;
268
269 rv = ::read(m_sock, buffRead, TELNET_BUFFSIZE);
270 if( rv < 0 ) return TTY_E_ERRORONREAD;
271 buffRead[rv] ='\0';
272
273 telnet_recv(m_telnet, buffRead, rv);
274 if(m_EHError != TTY_E_NOERROR) return m_EHError;
275
276 #ifdef TELNET_DEBUG
277 std::cerr << "telnetRead: read " << rv << " bytes. buffRead=" << buffRead << "\n";
278 #endif
279 }
280
281
282 return TTY_E_NOERROR;
283
284
285}
286
287int telnetConn::read( int timeoutRead,
288 bool clear
289 )
290{
291 return read(m_prompt, timeoutRead, clear);
292}
293
294int telnetConn::writeRead( const std::string & strWrite,
295 bool swallowEcho,
296 int timeoutWrite,
297 int timeoutRead
298 )
299{
300 m_strRead.clear();
301
302 int rv;
303
304 //Write First
305 rv = write( strWrite, timeoutWrite);
306 if(rv != TTY_E_NOERROR) return rv;
307
308 //Now read response from console
309 int timeoutCurrent;
310 double t0;
311
312 struct pollfd pfd;
313 pfd.fd = m_sock;
314 pfd.events = POLLIN;
315
316
317 //Start timeout clock for reading.
318 t0 = mx::sys::get_curr_time();;
319
320 if(swallowEcho)
321 {
322 char buffRead[TELNET_BUFFSIZE];
323
324 //First swallow the echo.
325 while( m_strRead.size() <= strWrite.size() )
326 {
327 timeoutCurrent = timeoutRead - (mx::sys::get_curr_time()-t0)*1000;
328 if(timeoutCurrent < 0) return TTY_E_TIMEOUTONREAD;
329
330 rv = poll( &pfd, 1, timeoutCurrent);
331 if( rv == 0 ) return TTY_E_TIMEOUTONREADPOLL;
332 if( rv < 0 ) return TTY_E_ERRORONREADPOLL;
333
334 rv = ::read(m_sock, buffRead, TELNET_BUFFSIZE);
335 if( rv < 0 ) return TTY_E_ERRORONREAD;
336
337 telnet_recv(m_telnet, buffRead, rv);
338 if(m_EHError != TTY_E_NOERROR) return m_EHError;
339 }
340
341 m_strRead.erase(0, strWrite.size());
342 }
343
345
346 timeoutCurrent = timeoutRead - (mx::sys::get_curr_time()-t0)*1000;
347 if(timeoutCurrent < 0) return TTY_E_TIMEOUTONREAD;
348
349 //Now read the response up to the eot.
350 return read(timeoutCurrent, false);
351}
352
353int telnetConn::send(int sock, const char *buffer, size_t size)
354{
355 /* send data */
356 while (size > 0)
357 {
358 int rs;
359
360 if ((rs = ::send(sock, buffer, size, 0)) == -1)
361 {
362 fprintf(stderr, "send() failed: %s\n", strerror(errno));
363 return TTY_E_ERRORONWRITE;
364 }
365 else if (rs == 0)
366 {
367 fprintf(stderr, "send() unexpectedly returned 0\n");
368 return TTY_E_ERRORONWRITE;
369 }
370 /* update pointer and size to see if we've got more to send */
371 buffer += rs;
372 size -= rs;
373 }
374
375 return TTY_E_NOERROR;
376}
377
378void telnetConn::event_handler( telnet_t *telnet,
379 telnet_event_t *ev,
380 void *user_data
381 )
382{
383 telnetConn * cs = static_cast<telnetConn*>(user_data);
384 int sock = cs->m_sock;
385
386 //Always reset the error at beginning.
387 cs->m_EHError = 0;
388
389 switch (ev->type)
390 {
391 /* data received */
392 case TELNET_EV_DATA:
393 {
394 #ifdef TELNET_DEBUG
395 std::cerr << "Got ev->data.size: " << ev->data.size << "\n";
396 #endif
397
398 //First we remove the various control chars from the front.
399 if(ev->data.size == 0) break;
400
401 char * buf = const_cast<char *>(ev->data.buffer);
402 buf[ev->data.size] = 0;
403
404 int nn = 0;
405 for(size_t i=0; i<ev->data.size; ++i)
406 {
407 if(ev->data.buffer[i] < 32)
408 {
409 ++buf;
410 ++nn;
411 continue;
412 }
413 break;
414 }
415
416 #ifdef TELNET_DEBUG
417 std::cerr << "removed: " << nn << "\n";
418 #endif
419
420 //Now we check for '\0' characters inside the data
421 //this is maybe a bug workaround for tripp lite pdu LX card . . .
422 int mm = 0;
423 for(size_t i=nn; i < ev->data.size;++i)
424 {
425 if( ev->data.buffer[i] == 0)
426 {
427 ++mm;
428 buf[i-nn] = '\n';
429 }
430 }
431
432 #ifdef TELNET_DEBUG
433 std::cerr << "dezeroed: " << mm << "\n";
434 #endif
435
436 //Now make it a string so we can make use of it.
437 std::string sbuf(buf);
438
439 if(sbuf.size() == 0) break;
440
441 #ifdef TELNET_DEBUG
442 std::cerr << "ev->data: " << sbuf << "\n";
443 #endif
444
445 if(cs->m_loggedin < TELNET_LOGGED_IN) //we aren't logged in yet
446 {
448 {
449 if(sbuf.find(cs->m_usernamePrompt) != std::string::npos)
450 {
452 }
453 break;
454 }
455
457 {
458 if( sbuf.find(cs->m_passwordPrompt) != std::string::npos)
459 {
461 }
462 break;
463 }
464
466 {
467 if( sbuf.find(cs->m_prompt) != std::string::npos)
468 {
470 }
471 break;
472 }
473 }
474
475 //Always append
476 cs->m_strRead += sbuf;
477 break;
478 }
479 /* data must be sent */
480 case TELNET_EV_SEND:
481 {
482 send(sock, ev->data.buffer, ev->data.size);
483 break;
484 }
485 /* request to enable remote feature (or receipt) */
486 case TELNET_EV_WILL:
487 {
488 /* we'll agree to turn off our echo if server wants us to stop */
489 //if (ev->neg.telopt == TELNET_TELOPT_ECHO) do_echo = 0;
490 break;
491 }
492 /* notification of disabling remote feature (or receipt) */
493 case TELNET_EV_WONT:
494 {
495 break;
496 }
497 /* request to enable local feature (or receipt) */
498 case TELNET_EV_DO:
499 {
500 break;
501 }
502 /* demand to disable local feature (or receipt) */
503 case TELNET_EV_DONT:
504 {
505 break;
506 }
507 /* respond to TTYPE commands */
508 case TELNET_EV_TTYPE:
509 {
510 /* respond with our terminal type, if requested */
511 if (ev->ttype.cmd == TELNET_TTYPE_SEND)
512 {
513 telnet_ttype_is(telnet, getenv("TERM"));
514 }
515 break;
516 }
517 /* respond to particular subnegotiations */
518 case TELNET_EV_SUBNEGOTIATION:
519 {
520 break;
521 }
522 /* error */
523 case TELNET_EV_ERROR:
524 {
525 fprintf(stderr, "ERROR: %s\n", ev->error.msg);
527 break;
528 }
529 default:
530 {
531 /* ignore */
532 break;
533 }
534 }
535}
536
537} //namespace tty
538} //namespace MagAOX
539
static const telnet_telopt_t telopts[]
libtelnet option table.
bool isEndOfTrans(const std::string &strRead, const std::string &eot)
Check if the end of the buffer contains the end-of-transmission string.
int telnetCRLF(std::string &telnetStr, const std::string &inputStr)
Replace lone \r and \n with \r\n for telnet-ness.
Definition dm.hpp:24
A Telnet connection manager, wrapping libtelnet.
int m_EHError
Used to indicate an error occurred in the event handler callback.
int login(const std::string &username, const std::string &password)
Manage the login process on this device.
std::string m_usernamePrompt
The device's username entry prompt, used for managing login.
int write(const std::string &buffWrite, int timeoutWrite)
Write to a telnet connection.
telnet_t * m_telnet
libtelnet telnet_t structure
~telnetConn()
D'tor, conducts connection cleanup.
static int send(int sock, const char *buffer, size_t size)
Internal send for use by event_handler.
std::string m_strRead
The accumulated string read from the device.
static void event_handler(telnet_t *telnet, telnet_event_t *ev, void *user_data)
Event handler callback for libtelnet processing.
int m_loggedin
Flag denoting the login state.
int m_sock
The socket file descriptor.
int noLogin()
Set flags as if we're logged in, used when device doesn't require it.
std::string m_passwordPrompt
The device's password entry prompt, used for managing login.
std::string m_prompt
The device's prompt, used for detecting end of transmission.
int writeRead(const std::string &strWrite, bool swallowEcho, int timeoutWrite, int timeoutRead)
Write to a telnet connection, then get the reply.
int read(const std::string &eot, int timeoutRead, bool clear=true)
Read from a telnet connection, until end-of-transmission string is read.
int connect(const std::string &host, const std::string &port)
Connect to the device.
Managing a connection to a telnet device.
#define TELNET_LOGGED_IN
#define TELNET_WAITING_USER
#define TELNET_WAITING_PROMPT
#define TELNET_BUFFSIZE
#define TELNET_GOT_PASS
#define TELNET_GOT_USER
#define TELNET_WAITING_PASS
#define TTY_E_TIMEOUTONREAD
Definition ttyErrors.hpp:27
#define TELNET_E_BIND
Definition ttyErrors.hpp:36
#define TTY_E_TIMEOUTONREADPOLL
Definition ttyErrors.hpp:24
#define TTY_E_ERRORONWRITE
Definition ttyErrors.hpp:22
#define TTY_E_ERRORONWRITEPOLL
Definition ttyErrors.hpp:21
#define TTY_E_ERRORONREADPOLL
Definition ttyErrors.hpp:25
#define TTY_E_ERRORONREAD
Definition ttyErrors.hpp:26
#define TELNET_E_TELNETINIT
Definition ttyErrors.hpp:38
#define TELNET_E_GETADDR
Definition ttyErrors.hpp:34
#define TTY_E_TIMEOUTONWRITEPOLL
Definition ttyErrors.hpp:20
#define TELNET_E_LOGINTIMEOUT
Definition ttyErrors.hpp:40
#define TTY_E_TIMEOUTONWRITE
Definition ttyErrors.hpp:23
#define TELNET_E_EHERROR
Definition ttyErrors.hpp:39
#define TTY_E_NOERROR
Definition ttyErrors.hpp:15
#define TELNET_E_CONNECT
Definition ttyErrors.hpp:37
#define TELNET_E_SOCKET
Definition ttyErrors.hpp:35