github.com/la5nta/wl2k-go@v0.11.8/transport/telnet/dial.go (about)

     1  // Copyright 2015 Martin Hebnes Pedersen (LA5NTA). All rights reserved.
     2  // Use of this source code is governed by the MIT-license that can be
     3  // found in the LICENSE file.
     4  
     5  // Package telnet provides a method of connecting to Winlink CMS over tcp ("telnet-mode")
     6  package telnet
     7  
     8  import (
     9  	"bufio"
    10  	"context"
    11  	"fmt"
    12  	"net"
    13  	"strings"
    14  	"time"
    15  
    16  	"github.com/la5nta/wl2k-go/transport"
    17  )
    18  
    19  const (
    20  	CMSTargetCall = "wl2k"
    21  	CMSPassword   = "CMSTelnet"
    22  	CMSAddress    = "server.winlink.org:8772"
    23  )
    24  
    25  var DefaultDialer = &Dialer{Timeout: 30 * time.Second}
    26  
    27  func init() {
    28  	transport.RegisterDialer("telnet", DefaultDialer)
    29  }
    30  
    31  // DialCMS dials a random CMS server through server.winlink.org.
    32  //
    33  // The function will retry 4 times before giving up and returning an error.
    34  func DialCMS(mycall string) (net.Conn, error) {
    35  	var conn net.Conn
    36  	var err error
    37  
    38  	// Dial with retry, in case we hit an unavailable CMS.
    39  	for i := 0; i < 4; i++ {
    40  		conn, err = Dial(CMSAddress, mycall, CMSPassword)
    41  		if err == nil {
    42  			break
    43  		}
    44  	}
    45  
    46  	return conn, err
    47  }
    48  
    49  // Dialer implements the transport.Dialer interface.
    50  type Dialer struct{ Timeout time.Duration }
    51  
    52  func (d Dialer) DialURLContext(ctx context.Context, url *transport.URL) (net.Conn, error) {
    53  	if url.Scheme != "telnet" {
    54  		return nil, transport.ErrUnsupportedScheme
    55  	}
    56  
    57  	var user, pass string
    58  	if url.User != nil {
    59  		pass, _ = url.User.Password()
    60  		user = url.User.Username()
    61  	}
    62  
    63  	timeout := d.Timeout
    64  	if str := url.Params.Get("dial_timeout"); str != "" {
    65  		dur, err := time.ParseDuration(str)
    66  		if err != nil {
    67  			return nil, fmt.Errorf("invalid dial_timeout value: %w", err)
    68  		}
    69  		timeout = dur
    70  	}
    71  	if timeout > 0 {
    72  		c, cancel := context.WithTimeout(ctx, timeout)
    73  		defer cancel()
    74  		ctx = c
    75  	}
    76  	return DialContext(ctx, url.Host, user, pass)
    77  }
    78  
    79  // DialURL dials telnet:// URLs
    80  //
    81  // The URL parameter dial_timeout can be used to set a custom dial timeout interval. E.g. "2m".
    82  func (d Dialer) DialURL(url *transport.URL) (net.Conn, error) {
    83  	return d.DialURLContext(context.Background(), url)
    84  }
    85  
    86  func Dial(addr, mycall, password string) (net.Conn, error) {
    87  	return DialTimeout(addr, mycall, password, 5*time.Second)
    88  }
    89  
    90  func DialTimeout(addr, mycall, password string, timeout time.Duration) (net.Conn, error) {
    91  	ctx, cancel := context.WithTimeout(context.Background(), timeout)
    92  	defer cancel()
    93  	return DialContext(ctx, addr, mycall, password)
    94  }
    95  
    96  func DialContext(ctx context.Context, addr, mycall, password string) (net.Conn, error) {
    97  	var d net.Dialer
    98  	conn, err := d.DialContext(ctx, `tcp`, addr)
    99  	if err != nil {
   100  		return nil, err
   101  	}
   102  
   103  	// Log in to telnet server
   104  	reader := bufio.NewReader(conn)
   105  L:
   106  	for {
   107  		line, err := reader.ReadString('\r')
   108  		line = strings.TrimSpace(strings.ToLower(line))
   109  		switch {
   110  		case err != nil:
   111  			conn.Close()
   112  			return nil, fmt.Errorf("Error while logging in: %s", err)
   113  		case strings.HasPrefix(line, "callsign"):
   114  			fmt.Fprintf(conn, "%s\r", mycall)
   115  		case strings.HasPrefix(line, "password"):
   116  			fmt.Fprintf(conn, "%s\r", password)
   117  			break L
   118  		}
   119  	}
   120  
   121  	return &Conn{conn, CMSTargetCall}, nil
   122  }