github.com/la5nta/wl2k-go@v0.11.8/transport/ax25/ax25_linux.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 //go:build libax25 && cgo 6 // +build libax25,cgo 7 8 package ax25 9 10 /* 11 #include <sys/socket.h> 12 #include <netax25/ax25.h> 13 #include <netax25/axlib.h> 14 #include <netax25/axconfig.h> 15 #include <fcntl.h> 16 */ 17 import "C" 18 19 import ( 20 "context" 21 "errors" 22 "fmt" 23 "io" 24 "net" 25 "os" 26 "syscall" 27 "time" 28 "unsafe" 29 ) 30 31 type ax25Addr C.struct_full_sockaddr_ax25 32 33 var numAXPorts int 34 35 // bug(martinhpedersen): The AX.25 stack does not support SOCK_STREAM, so any write to the connection 36 // that is larger than maximum packet length will fail. The b2f impl. requires 125 bytes long packets. 37 var ErrMessageTooLong = errors.New("Write: Message too long. Consider increasing maximum packet length to >= 125.") 38 var ErrPortNotExist = errors.New("No such AX port found") 39 40 type fd uintptr 41 42 type ax25Listener struct { 43 sock fd 44 localAddr AX25Addr 45 close chan struct{} 46 } 47 48 func portExists(port string) bool { return C.ax25_config_get_dev(C.CString(port)) != nil } 49 50 func loadPorts() (int, error) { 51 if numAXPorts > 0 { 52 return numAXPorts, nil 53 } 54 55 n, err := C.ax25_config_load_ports() 56 if err != nil { 57 return int(n), err 58 } else if n == 0 { 59 return 0, fmt.Errorf("No AX.25 ports configured") 60 } 61 62 numAXPorts = int(n) 63 return numAXPorts, err 64 } 65 66 func checkPort(axPort string) error { 67 if axPort == "" { 68 return errors.New("Invalid empty axport") 69 } 70 if _, err := loadPorts(); err != nil { 71 return err 72 } 73 if !portExists(axPort) { 74 return ErrPortNotExist 75 } 76 return nil 77 } 78 79 // Addr returns the listener's network address, an AX25Addr. 80 func (ln ax25Listener) Addr() net.Addr { return ln.localAddr } 81 82 // Close stops listening on the AX.25 port. Already Accepted connections are not closed. 83 func (ln ax25Listener) Close() error { close(ln.close); return ln.sock.close() } 84 85 // Accept waits for the next call and returns a generic Conn. 86 // 87 // See net.Listener for more information. 88 func (ln ax25Listener) Accept() (net.Conn, error) { 89 err := ln.sock.waitRead(ln.close) 90 if err != nil { 91 return nil, err 92 } 93 94 nfd, addr, err := ln.sock.accept() 95 if err != nil { 96 return nil, err 97 } 98 99 conn := &Conn{ 100 localAddr: ln.localAddr, 101 remoteAddr: AX25Addr{addr}, 102 ReadWriteCloser: os.NewFile(uintptr(nfd), ""), 103 } 104 105 return conn, nil 106 } 107 108 // ListenAX25 announces on the local port axPort using mycall as the local address. 109 // 110 // An error will be returned if axPort is empty. 111 func ListenAX25(axPort, mycall string) (net.Listener, error) { 112 if err := checkPort(axPort); err != nil { 113 return nil, err 114 } 115 116 // Setup local address (via callsign of supplied axPort) 117 localAddr := newAX25Addr(mycall) 118 if err := localAddr.setPort(axPort); err != nil { 119 return nil, err 120 } 121 122 // Create file descriptor 123 var socket fd 124 if f, err := syscall.Socket(syscall.AF_AX25, syscall.SOCK_SEQPACKET, 0); err != nil { 125 return nil, err 126 } else { 127 socket = fd(f) 128 } 129 130 if err := socket.bind(localAddr); err != nil { 131 return nil, err 132 } 133 if err := syscall.Listen(int(socket), syscall.SOMAXCONN); err != nil { 134 return nil, err 135 } 136 137 return ax25Listener{ 138 sock: fd(socket), 139 localAddr: AX25Addr{localAddr}, 140 close: make(chan struct{}), 141 }, nil 142 } 143 144 func DialAX25Context(ctx context.Context, axPort, mycall, targetcall string) (*Conn, error) { 145 if err := checkPort(axPort); err != nil { 146 return nil, err 147 } 148 149 // Setup local address (via callsign of supplied axPort) 150 localAddr := newAX25Addr(mycall) 151 if err := localAddr.setPort(axPort); err != nil { 152 return nil, err 153 } 154 remoteAddr := newAX25Addr(targetcall) 155 156 // Create file descriptor 157 var socket fd 158 if f, err := syscall.Socket(syscall.AF_AX25, syscall.SOCK_SEQPACKET, 0); err != nil { 159 return nil, err 160 } else { 161 socket = fd(f) 162 } 163 164 // Bind 165 if err := socket.bind(localAddr); err != nil { 166 return nil, err 167 } 168 169 // Connect 170 err := socket.connectContext(ctx, remoteAddr) 171 if err != nil { 172 socket.close() 173 return nil, err 174 } 175 176 return &Conn{ 177 ReadWriteCloser: os.NewFile(uintptr(socket), axPort), 178 localAddr: AX25Addr{localAddr}, 179 remoteAddr: AX25Addr{remoteAddr}, 180 }, nil 181 } 182 183 // DialAX25Timeout acts like DialAX25 but takes a timeout. 184 func DialAX25Timeout(axPort, mycall, targetcall string, timeout time.Duration) (*Conn, error) { 185 ctx, cancel := context.WithTimeout(context.Background(), timeout) 186 defer cancel() 187 conn, err := DialAX25Context(ctx, axPort, mycall, targetcall) 188 if err != nil && errors.Is(ctx.Err(), context.DeadlineExceeded) { 189 // Local timeout reached. 190 err = fmt.Errorf("Dial timeout") 191 } 192 return conn, err 193 } 194 195 func (c *Conn) Close() error { 196 if !c.ok() { 197 return syscall.EINVAL 198 } 199 200 return c.ReadWriteCloser.Close() 201 } 202 203 func (c *Conn) Write(p []byte) (n int, err error) { 204 if !c.ok() { 205 return 0, syscall.EINVAL 206 } 207 208 n, err = c.ReadWriteCloser.Write(p) 209 perr, ok := err.(*os.PathError) 210 if !ok { 211 return 212 } 213 214 switch perr.Err.Error() { 215 case "message too long": 216 return n, ErrMessageTooLong 217 default: 218 return 219 } 220 } 221 222 func (c *Conn) Read(p []byte) (n int, err error) { 223 if !c.ok() { 224 return 0, syscall.EINVAL 225 } 226 227 n, err = c.ReadWriteCloser.Read(p) 228 perr, ok := err.(*os.PathError) 229 if !ok { 230 return 231 } 232 233 // TODO: These errors should not be checked using string comparison! 234 // The weird error handling here is needed because of how the *os.File treats 235 // the underlying fd. This should be fixed the same way as net.FileConn does. 236 switch perr.Err.Error() { 237 case "transport endpoint is not connected": // We get this error when the remote hangs up 238 return n, io.EOF 239 default: 240 return 241 } 242 } 243 244 // DialAX25 connects to the remote station targetcall using the named axport and mycall. 245 // 246 // An error will be returned if axPort is empty. 247 func DialAX25(axPort, mycall, targetcall string) (*Conn, error) { 248 return DialAX25Context(context.Background(), axPort, mycall, targetcall) 249 } 250 251 func (sock fd) connectContext(ctx context.Context, addr ax25Addr) (err error) { 252 if err = syscall.SetNonblock(int(sock), true); err != nil { 253 return err 254 } 255 defer syscall.SetNonblock(int(sock), false) 256 257 err = sock.connect(addr) 258 if err == nil { 259 return nil // Connected 260 } else if err != syscall.EINPROGRESS { 261 return err 262 } 263 264 // Wait for response as long as the dial context is valid. 265 for { 266 if ctx.Err() != nil { 267 sock.close() 268 return ctx.Err() 269 } 270 fdset := new(syscall.FdSet) 271 maxFd := fdSet(fdset, int(sock)) 272 tv := syscall.NsecToTimeval(int64(10 * time.Millisecond)) 273 n, err := syscall.Select(maxFd+1, nil, fdset, nil, &tv) 274 switch { 275 case n < 0 && err != syscall.EINTR: 276 sock.close() 277 return err 278 case n > 0: 279 // Verify that connection is OK 280 nerr, err := syscall.GetsockoptInt(int(sock), syscall.SOL_SOCKET, syscall.SO_ERROR) 281 if err != nil { 282 sock.close() 283 return err 284 } 285 err = syscall.Errno(nerr) 286 if nerr != 0 && err != syscall.EINPROGRESS && err != syscall.EALREADY && err != syscall.EINTR { 287 sock.close() 288 return err 289 } 290 return nil // Connected 291 default: 292 // Nothing has changed yet. Keep looping. 293 continue 294 } 295 } 296 } 297 298 // waitRead blocks until the socket is ready for read or the call is canceled 299 // 300 // The error syscall.EINVAL is returned if the cancel channel is closed, indicating 301 // that the socket is being closed by another thread. 302 func (sock fd) waitRead(cancel <-chan struct{}) error { 303 pr, pw, err := os.Pipe() 304 if err != nil { 305 return err 306 } 307 308 done := make(chan struct{}) 309 go func() { 310 select { 311 case <-cancel: 312 pw.Write([]byte("\n")) 313 case <-done: 314 return 315 } 316 }() 317 defer func() { close(done); pw.Close() }() 318 319 fdset := new(syscall.FdSet) 320 maxFd := fdSet(fdset, int(sock), int(pr.Fd())) 321 322 syscall.SetNonblock(int(sock), true) 323 defer func() { syscall.SetNonblock(int(sock), false) }() 324 325 var n int 326 for { 327 n, err = syscall.Select(maxFd+1, fdset, nil, nil, nil) 328 if n < 0 || err != nil { 329 return err 330 } 331 332 if fdIsSet(fdset, int(sock)) { 333 break // sock is ready for read 334 } else { 335 return syscall.EINVAL 336 } 337 } 338 return nil 339 } 340 341 func (sock fd) close() error { 342 return syscall.Close(int(sock)) 343 } 344 345 func (sock fd) accept() (nfd fd, addr ax25Addr, err error) { 346 addrLen := C.socklen_t(unsafe.Sizeof(addr)) 347 n, err := C.accept( 348 C.int(sock), 349 (*C.struct_sockaddr)(unsafe.Pointer(&addr)), 350 &addrLen) 351 352 if addrLen != C.socklen_t(unsafe.Sizeof(addr)) { 353 panic("unexpected socklet_t") 354 } 355 356 return fd(n), addr, err 357 } 358 359 func (sock fd) connect(addr ax25Addr) (err error) { 360 _, err = C.connect( 361 C.int(sock), 362 (*C.struct_sockaddr)(unsafe.Pointer(&addr)), 363 C.socklen_t(unsafe.Sizeof(addr))) 364 365 return 366 } 367 368 func (sock fd) bind(addr ax25Addr) (err error) { 369 _, err = C.bind( 370 C.int(sock), 371 (*C.struct_sockaddr)(unsafe.Pointer(&addr)), 372 C.socklen_t(unsafe.Sizeof(addr))) 373 374 return 375 } 376 377 type ax25_address *C.ax25_address 378 379 func (a ax25Addr) Address() Address { 380 return AddressFromString( 381 C.GoString(C.ax25_ntoa(a.ax25_address())), 382 ) 383 } 384 385 func (a ax25Addr) Digis() []Address { 386 digis := make([]Address, a.numDigis()) 387 for i, digi := range a.digis() { 388 digis[i] = AddressFromString(C.GoString(C.ax25_ntoa(digi))) 389 } 390 return digis 391 } 392 393 func (a *ax25Addr) numDigis() int { 394 return int(a.fsa_ax25.sax25_ndigis) 395 } 396 397 func (a *ax25Addr) digis() []ax25_address { 398 digis := make([]ax25_address, a.numDigis()) 399 for i := range digis { 400 digis[i] = (*C.ax25_address)(unsafe.Pointer(&a.fsa_digipeater[i])) 401 } 402 return digis 403 } 404 405 func (a *ax25Addr) ax25_address() ax25_address { 406 return (*C.ax25_address)(unsafe.Pointer(&a.fsa_ax25.sax25_call.ax25_call)) 407 } 408 409 func (a *ax25Addr) setPort(port string) (err error) { 410 C.ax25_aton_entry( 411 C.ax25_config_get_addr(C.CString(port)), 412 &a.fsa_digipeater[0].ax25_call[0], 413 ) 414 a.fsa_ax25.sax25_ndigis = 1 415 return 416 } 417 418 func newAX25Addr(address string) ax25Addr { 419 var addr C.struct_full_sockaddr_ax25 420 421 if C.ax25_aton(C.CString(address), &addr) < 0 { 422 panic("ax25_aton") 423 } 424 addr.fsa_ax25.sax25_family = syscall.AF_AX25 425 426 return ax25Addr(addr) 427 } 428 429 func fdSet(p *syscall.FdSet, fd ...int) (max int) { 430 // Shamelessly stolen from src/pkg/exp/inotify/inotify_linux.go: 431 // 432 // Create fdSet, taking into consideration that 433 // 64-bit OS uses Bits: [16]int64, while 32-bit OS uses Bits: [32]int32. 434 // This only support File Descriptors up to 1024 435 // 436 fElemSize := 32 * 32 / len(p.Bits) 437 438 for _, i := range fd { 439 if i > 1024 { 440 panic(fmt.Errorf("fdSet: File Descriptor >= 1024: %v", i)) 441 } 442 if i > max { 443 max = i 444 } 445 p.Bits[i/fElemSize] |= 1 << uint(i%fElemSize) 446 } 447 return max 448 } 449 450 func fdIsSet(p *syscall.FdSet, i int) bool { 451 fElemSize := 32 * 32 / len(p.Bits) 452 return p.Bits[i/fElemSize]&(1<<uint(i%fElemSize)) != 0 453 }