@@ -46,11 +46,16 @@ func Register(router *msgpackrouter.Router) {
4646
4747 _ = router .RegisterMethod ("tcp/connectSSL" , tcpConnectSSL )
4848
49+ _ = router .RegisterMethod ("udp/connect" , udpConnect )
50+ _ = router .RegisterMethod ("udp/write" , udpWrite )
51+ _ = router .RegisterMethod ("udp/read" , udpRead )
52+ _ = router .RegisterMethod ("udp/close" , udpClose )
4953}
5054
5155var lock sync.RWMutex
5256var liveConnections = make (map [uint ]net.Conn )
5357var liveListeners = make (map [uint ]net.Listener )
58+ var liveUdpConnections = make (map [uint ]net.PacketConn )
5459var nextConnectionID atomic.Uint32
5560
5661// takeLockAndGenerateNextID generates a new unique ID for a connection or listener.
@@ -328,3 +333,146 @@ func tcpConnectSSL(ctx context.Context, rpc *msgpackrpc.Connection, params []any
328333 unlock ()
329334 return id , nil
330335}
336+
337+ func udpConnect (ctx context.Context , rpc * msgpackrpc.Connection , params []any ) (_result any , _err any ) {
338+ if len (params ) != 2 {
339+ return nil , []any {1 , "Invalid number of parameters, expected server address and port" }
340+ }
341+ serverAddr , ok := params [0 ].(string )
342+ if ! ok {
343+ return nil , []any {1 , "Invalid parameter type, expected string for server address" }
344+ }
345+ serverPort , ok := msgpackrpc .ToUint (params [1 ])
346+ if ! ok {
347+ return nil , []any {1 , "Invalid parameter type, expected uint16 for server port" }
348+ }
349+
350+ serverAddr = net .JoinHostPort (serverAddr , strconv .Itoa (int (serverPort )))
351+ udpAddr , err := net .ResolveUDPAddr ("udp" , serverAddr )
352+ if err != nil {
353+ return nil , []any {2 , "Failed to resolve UDP address: " + err .Error ()}
354+ }
355+ udpConn , err := net .ListenUDP ("udp" , udpAddr )
356+ if err != nil {
357+ return nil , []any {2 , "Failed to connect to server: " + err .Error ()}
358+ }
359+
360+ // Successfully opened UDP channel
361+
362+ id , unlock := takeLockAndGenerateNextID ()
363+ liveUdpConnections [id ] = udpConn
364+ unlock ()
365+ return id , nil
366+ }
367+
368+ func udpWrite (ctx context.Context , rpc * msgpackrpc.Connection , params []any ) (_result any , _err any ) {
369+ if len (params ) != 4 {
370+ return nil , []any {1 , "Invalid number of parameters, expected udpConnId, dest address, dest port, payload" }
371+ }
372+ id , ok := msgpackrpc .ToUint (params [0 ])
373+ if ! ok {
374+ return nil , []any {1 , "Invalid parameter type, expected int for UDP connection ID" }
375+ }
376+ targetIP , ok := params [1 ].(string )
377+ if ! ok {
378+ return nil , []any {1 , "Invalid parameter type, expected string for server address" }
379+ }
380+ targetPort , ok := msgpackrpc .ToUint (params [2 ])
381+ if ! ok {
382+ return nil , []any {1 , "Invalid parameter type, expected uint16 for server port" }
383+ }
384+ data , ok := params [3 ].([]byte )
385+ if ! ok {
386+ if dataStr , ok := params [3 ].(string ); ok {
387+ data = []byte (dataStr )
388+ } else {
389+ // If data is not []byte or string, return an error
390+ return nil , []any {1 , "Invalid parameter type, expected []byte or string for data to write" }
391+ }
392+ }
393+
394+ lock .RLock ()
395+ udpConn , ok := liveUdpConnections [id ]
396+ lock .RUnlock ()
397+ if ! ok {
398+ return nil , []any {2 , fmt .Sprintf ("UDP connection not found for ID: %d" , id )}
399+ }
400+
401+ targetAddr := net .JoinHostPort (targetIP , strconv .Itoa (int (targetPort )))
402+ addr , err := net .ResolveUDPAddr ("udp" , targetAddr ) // TODO: This is inefficient, implement some caching
403+ if err != nil {
404+ return nil , []any {3 , "Failed to resolve target address: " + err .Error ()}
405+ }
406+ if n , err := udpConn .WriteTo (data , addr ); err != nil {
407+ return nil , []any {4 , "Failed to write to UDP connection: " + err .Error ()}
408+ } else {
409+ return n , nil
410+ }
411+ }
412+
413+ func udpRead (ctx context.Context , rpc * msgpackrpc.Connection , params []any ) (_result any , _err any ) {
414+ if len (params ) != 2 {
415+ return nil , []any {1 , "Invalid number of parameters, expected (UDP connection ID, max bytes to read)" }
416+ }
417+ id , ok := msgpackrpc .ToUint (params [0 ])
418+ if ! ok {
419+ return nil , []any {1 , "Invalid parameter type, expected uint for UDP connection ID" }
420+ }
421+ lock .RLock ()
422+ udpConn , ok := liveUdpConnections [id ]
423+ lock .RUnlock ()
424+ if ! ok {
425+ return nil , []any {2 , fmt .Sprintf ("UDP connection not found for ID: %d" , id )}
426+ }
427+ maxBytes , ok := msgpackrpc .ToUint (params [1 ])
428+ if ! ok {
429+ return nil , []any {1 , "Invalid parameter type, expected uint for max bytes to read" }
430+ }
431+
432+ buffer := make ([]byte , maxBytes )
433+
434+ n , addr , err := udpConn .ReadFrom (buffer )
435+ if err != nil {
436+ return nil , []any {3 , "Failed to read from UDP connection: " + err .Error ()}
437+ }
438+ host , portStr , err := net .SplitHostPort (addr .String ())
439+ if err != nil {
440+ // Should never fail, but...
441+ return nil , []any {4 , "Failed to parse source address: " + err .Error ()}
442+ }
443+ port , err := strconv .Atoi (portStr )
444+ if err != nil {
445+ // Should never fail, but...
446+ return nil , []any {4 , "Failed to parse source address: " + err .Error ()}
447+ }
448+ return []any {buffer [:n ], host , port }, nil
449+ }
450+
451+ func udpClose (ctx context.Context , rpc * msgpackrpc.Connection , params []any ) (_result any , _err any ) {
452+ if len (params ) != 1 {
453+ return nil , []any {1 , "Invalid number of parameters, expected UDP connection ID" }
454+ }
455+ id , ok := msgpackrpc .ToUint (params [0 ])
456+ if ! ok {
457+ return nil , []any {1 , "Invalid parameter type, expected int for UDP connection ID" }
458+ }
459+
460+ lock .Lock ()
461+ udpConn , existsConn := liveUdpConnections [id ]
462+ if existsConn {
463+ delete (liveUdpConnections , id )
464+ }
465+ lock .Unlock ()
466+
467+ if ! existsConn {
468+ return nil , []any {2 , fmt .Sprintf ("UDP connection not found for ID: %d" , id )}
469+ }
470+
471+ // Close the connection if it exists
472+ // We do not return an error to the caller if the close operation fails, as it is not critical,
473+ // but we only log the error for debugging purposes.
474+ if err := udpConn .Close (); err != nil {
475+ return err .Error (), nil
476+ }
477+ return "" , nil
478+ }
0 commit comments