Skip to content
telnet.c 35.4 KiB
Newer Older
Daniel Stenberg's avatar
Daniel Stenberg committed
      
      case CURL_TS_DONT:
        printoption(data, "RCVD", CURL_DONT, c);
        tn->please_negotiate = 1;
        rec_dont(conn, c);
Daniel Stenberg's avatar
Daniel Stenberg committed

Daniel Stenberg's avatar
Daniel Stenberg committed

          {
            /*
             * This is an error.  We only expect to get
             * "IAC IAC" or "IAC SE".  Several things may
             * have happend.  An IAC was not doubled, the
             * IAC SE was left off, or another option got
             * inserted into the suboption are all possibilities.
             * If we assume that the IAC was not doubled,
             * and really the IAC SE was left off, we could
             * get into an infinate loop here.  So, instead,
             * we terminate the suboption, and process the
             * partial suboption if we can.
             */
            CURL_SB_ACCUM(tn, (unsigned char)CURL_IAC);
            CURL_SB_ACCUM(tn, c);
            tn->subpointer -= 2;
Daniel Stenberg's avatar
Daniel Stenberg committed
	    
            printoption(data, "In SUBOPTION processing, RCVD", CURL_IAC, c);
            suboption(conn);   /* handle sub-option */
          CURL_SB_ACCUM(tn,c);
          tn->telrcv_state = CURL_TS_SB;
          CURL_SB_ACCUM(tn, (unsigned char)CURL_IAC);
          CURL_SB_ACCUM(tn, (unsigned char)CURL_SE);
          tn->subpointer -= 2;
          suboption(conn);   /* handle sub-option */
CURLcode Curl_telnet_done(struct connectdata *conn)
Daniel Stenberg's avatar
Daniel Stenberg committed
{
  struct TELNET *tn = (struct TELNET *)conn->proto.telnet;
  curl_slist_free_all(tn->telnet_vars);
Daniel Stenberg's avatar
Daniel Stenberg committed

  free(conn->proto.telnet);
  conn->proto.telnet = NULL;

CURLcode Curl_telnet(struct connectdata *conn)
  struct SessionHandle *data = conn->data;
  curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
  HMODULE wsock2;
  WSOCK2_FUNC close_event_func;
  WSOCK2_FUNC create_event_func;
  WSOCK2_FUNC event_select_func;
  WSOCK2_FUNC enum_netevents_func;
  WSAEVENT event_handle;
  WSANETWORKEVENTS events;
  HANDLE stdin_handle;
  HANDLE objs[2];         
  DWORD  obj_count;
  DWORD  wait_timeout;
  fd_set readfd;
  fd_set keepfd;
  bool keepon = TRUE;
  struct TELNET *tn;

  code = init_telnet(conn);
  if(code)
    return code;

  tn = (struct TELNET *)conn->proto.telnet;
Daniel Stenberg's avatar
Daniel Stenberg committed

  code = check_telnet_options(conn);
  if(code)
    return code;
  /*
  ** This functionality only works with WinSock >= 2.0.  So,
  ** make sure have it.
  */
  code = check_wsock2(data);
  if (code)
    return code;

  /* OK, so we have WinSock 2.0.  We need to dynamically */
  /* load ws2_32.dll and get the function pointers we need. */
  wsock2 = LoadLibrary("WS2_32.DLL");
  if (wsock2 == NULL) {
    failf(data,"failed to load WS2_32.DLL (%d)",GetLastError());
    return CURLE_FAILED_INIT;
  }

  /* Grab a pointer to WSACreateEvent */
  create_event_func = GetProcAddress(wsock2,"WSACreateEvent");
  if (create_event_func == NULL) {
    failf(data,"failed to find WSACreateEvent function (%d)",
	  GetLastError());
    FreeLibrary(wsock2);
    return CURLE_FAILED_INIT;
  }

  /* And WSACloseEvent */
  close_event_func = GetProcAddress(wsock2,"WSACloseEvent");
  if (create_event_func == NULL) {
    failf(data,"failed to find WSACloseEvent function (%d)",
	  GetLastError());
    FreeLibrary(wsock2);
    return CURLE_FAILED_INIT;
  }

  /* And WSAEventSelect */
  event_select_func = GetProcAddress(wsock2,"WSAEventSelect");
  if (event_select_func == NULL) {
    failf(data,"failed to find WSAEventSelect function (%d)",
	  GetLastError());
    FreeLibrary(wsock2);
    return CURLE_FAILED_INIT;
  }

  /* And WSAEnumNetworkEvents */
  enum_netevents_func = GetProcAddress(wsock2,"WSAEnumNetworkEvents");
  if (enum_netevents_func == NULL) {
    failf(data,"failed to find WSAEnumNetworkEvents function (%d)",
	  GetLastError());
    FreeLibrary(wsock2);
    return CURLE_FAILED_INIT;
  }

  /* We want to wait for both stdin and the socket. Since
  ** the select() function in winsock only works on sockets
  ** we have to use the WaitForMultipleObjects() call.
  */

  /* First, create a sockets event object */
  event_handle = (WSAEVENT)create_event_func();
  if (event_handle == WSA_INVALID_EVENT) {
    failf(data,"WSACreateEvent failed (%d)",WSAGetLastError());
    FreeLibrary(wsock2);
    return CURLE_FAILED_INIT;
  }

  /* The get the Windows file handle for stdin */
  stdin_handle = GetStdHandle(STD_INPUT_HANDLE);

  /* Create the list of objects to wait for */
  objs[0] = event_handle;
  objs[1] = stdin_handle;

  /* Tell winsock what events we want to listen to */
  if(event_select_func(sockfd, event_handle, FD_READ|FD_CLOSE) == SOCKET_ERROR) {
    close_event_func(event_handle);
    FreeLibrary(wsock2);
                                   
  /* If stdin_handle is a pipe, use PeekNamedPipe() method to check it, 
     else use the old WaitForMultipleObjects() way */         
  if(GetFileType(stdin_handle) == FILE_TYPE_PIPE) {
    /* Don't wait for stdin_handle, just wait for event_handle */
    obj_count = 1;      
    /* Check stdin_handle per 100 milliseconds */
    wait_timeout = 100;  
  } else {
    obj_count = 2;
    wait_timeout = INFINITE;
  }
  
  /* Keep on listening and act on events */
  while(keepon) {
    waitret = WaitForMultipleObjects(obj_count, objs, FALSE, wait_timeout);
    switch(waitret) {
    case WAIT_TIMEOUT:
    {
      unsigned char outbuf[2];
      int out_count = 0;
      ssize_t bytes_written;
      char *buffer = buf;

      for(;;) {
        if(!PeekNamedPipe(stdin_handle,NULL,0,NULL,&nread,NULL)) {
          keepon = FALSE;
          break;
        }
        
        if(!nread)
          break;
          
        if(!ReadFile(stdin_handle, buf, sizeof(data->state.buffer),
                     &readfile_read, NULL)) {
          keepon = FALSE;
          break;
        }
        nread = readfile_read;
        
        while(nread--) {
          outbuf[0] = *buffer++;
          out_count = 1;
          if(outbuf[0] == CURL_IAC)
            outbuf[out_count++] = CURL_IAC;
            
          Curl_write(conn, conn->sock[FIRSTSOCKET], outbuf,
                     out_count, &bytes_written);
        }
      }
    }      
    break;
    
    case WAIT_OBJECT_0 + 1:
      unsigned char outbuf[2];
      int out_count = 0;
      ssize_t bytes_written;
      char *buffer = buf;
      if(!ReadFile(stdin_handle, buf, sizeof(data->state.buffer),
      while(nread--) {
        outbuf[0] = *buffer++;
        out_count = 1;
        if(outbuf[0] == CURL_IAC)
          outbuf[out_count++] = CURL_IAC;
        Curl_write(conn, conn->sock[FIRSTSOCKET], outbuf,
                   out_count, &bytes_written);
      if(enum_netevents_func(sockfd, event_handle, &events)
         != SOCKET_ERROR) {
        if(events.lNetworkEvents & FD_READ) {
          /* This reallu OUGHT to check its return code. */
          (void)Curl_read(conn, sockfd, buf, BUFSIZE - 1, &nread);
          telrcv(conn, (unsigned char *)buf, nread);
          
          fflush(stdout);
          /* Negotiate if the peer has started negotiating,
             otherwise don't. We don't want to speak telnet with
             non-telnet servers, like POP or SMTP. */
          if(tn->please_negotiate && !tn->already_negotiated) {
            negotiate(conn);
            tn->already_negotiated = 1;
        if(events.lNetworkEvents & FD_CLOSE) {
          keepon = FALSE;

  /* We called WSACreateEvent, so call WSACloseEvent */
  if (close_event_func(event_handle) == FALSE) {
    infof(data,"WSACloseEvent failed (%d)",WSAGetLastError());
  }

  /* "Forget" pointers into the library we're about to free */
  create_event_func = NULL;
  close_event_func = NULL;
  event_select_func = NULL;
  enum_netevents_func = NULL;

  /* We called LoadLibrary, so call FreeLibrary */
  if (!FreeLibrary(wsock2))
    infof(data,"FreeLibrary(wsock2) failed (%d)",GetLastError());
  FD_ZERO (&readfd);		/* clear it */
  FD_SET (sockfd, &readfd);
  FD_SET (0, &readfd);
Daniel Stenberg's avatar
Daniel Stenberg committed

  keepfd = readfd;
Daniel Stenberg's avatar
Daniel Stenberg committed

  while (keepon) {
    readfd = keepfd;		/* set this every lap in the loop */
    interval.tv_sec = 1;
    interval.tv_usec = 0;
Daniel Stenberg's avatar
Daniel Stenberg committed

    switch (select (sockfd + 1, &readfd, NULL, NULL, &interval)) {
    case -1:			/* error, stop reading */
      keepon = FALSE;
      continue;
    case 0:			/* timeout */
      break;
    default:			/* read! */
      if(FD_ISSET(0, &readfd)) { /* read from stdin */
        unsigned char outbuf[2];
        int out_count = 0;
        ssize_t bytes_written;
        char *buffer = buf;
        
        nread = read(0, buf, 255);

        while(nread--) {
          outbuf[0] = *buffer++;
          out_count = 1;
          if(outbuf[0] == CURL_IAC)
            outbuf[out_count++] = CURL_IAC;
          Curl_write(conn, conn->sock[FIRSTSOCKET], outbuf,
                     out_count, &bytes_written);
        }
      }
Daniel Stenberg's avatar
Daniel Stenberg committed

      if(FD_ISSET(sockfd, &readfd)) {
        /* This OUGHT to check the return code... */
        (void)Curl_read(conn, sockfd, buf, BUFSIZE - 1, &nread);
Daniel Stenberg's avatar
Daniel Stenberg committed

        /* if we receive 0 or less here, the server closed the connection and
           we bail out from this! */
        if (nread <= 0) {
          keepon = FALSE;
          break;
        }
        telrcv(conn, (unsigned char *)buf, nread);

        /* Negotiate if the peer has started negotiating,
           otherwise don't. We don't want to speak telnet with
           non-telnet servers, like POP or SMTP. */
        if(tn->please_negotiate && !tn->already_negotiated) {
          tn->already_negotiated = 1;
    if(data->set.timeout) {
      struct timeval now;           /* current time */
      now = Curl_tvnow();
      if(Curl_tvdiff(now, conn->created)/1000 >= data->set.timeout) {
        failf(data, "Time-out");
        code = CURLE_OPERATION_TIMEOUTED;
        keepon = FALSE;
      }
    }
  /* mark this as "no further transfer wanted" */
  Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL);

  return code;
Daniel Stenberg's avatar
Daniel Stenberg committed
}