### Example Usage: Listening for Events Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/MANIFEST.txt Demonstrates how to set up a listener to receive real-time events from the RouterOS device. ```go package main import ( "context" "fmt" "log" "github.com/go-routeros/routeros/v2" ) func main() { ctx, cancel := context.WithCancel(context.Background()) defer cancel() client, err := routeros.Dial(ctx, "tcp", "user:password@192.168.88.1:8728", nil) if err != nil { log.Fatalf("Failed to connect: %v", err) } defer client.Close() // Start listening for events (e.g., interface status changes) go func() { ch, err := client.Listen(ctx, "/interface", "", nil) if err != nil { log.Printf("Failed to start listening: %v\n", err) return } for reply := range ch { if reply.Data != nil { fmt.Printf("Event received: %v\n", reply.Data) } } }() // Keep the main goroutine alive to receive events <-ctx.Done() fmt.Println("Listener stopped.") } ``` -------------------------------- ### Example Usage: Handling Tabbed Output Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/MANIFEST.txt Illustrates how to process commands that return data in a tabbed format. ```go package main import ( "context" "fmt" "log" "github.com/go-routeros/routeros/v2" ) func main() { ctx, cancel := context.WithCancel(context.Background()) defer cancel() client, err := routeros.Dial(ctx, "tcp", "user:password@192.168.88.1:8728", nil) if err != nil { log.Fatalf("Failed to connect: %v", err) } defer client.Close() // Example: Get interface details with tabbed output reply, err := client.RunArgs(ctx, "/interface/print", "?detail") if err != nil { log.Fatalf("Failed to run command: %v", err) } if len(reply.Data) > 0 { fmt.Println("Interface Details:") for _, item := range reply.Data { fmt.Printf(" Name: %s, Type: %s, MAC Address: %s\n", item["name"], item["type"], item["mac-address"]) } } } ``` -------------------------------- ### Example Usage: Basic Command Execution Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/MANIFEST.txt Illustrates how to establish a connection, send a command, and process the reply using the client library. ```go package main import ( "context" "fmt" "log" "github.com/go-routeros/routeros/v2" ) func main() { ctx, cancel := context.WithCancel(context.Background()) defer cancel() client, err := routeros.Dial(ctx, "tcp", "user:password@192.168.88.1:8728", nil) if err != nil { log.Fatalf("Failed to connect: %v", err) } defer client.Close() // Ensure connection is closed // Example: Get system identity reply, err := client.RunArgs(ctx, "/system/identity/print") if err != nil { log.Fatalf("Failed to run command: %v", err) } fmt.Printf("System Identity: %s\n", reply.Data[0]["name"]) } ``` -------------------------------- ### Example Usage: Asynchronous Command Execution Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/MANIFEST.txt Shows how to execute commands asynchronously and handle replies using channels. ```go package main import ( "context" "fmt" "log" "github.com/go-routeros/routeros/v2" ) func main() { ctx, cancel := context.WithCancel(context.Background()) defer cancel() client, err := routeros.Dial(ctx, "tcp", "user:password@192.168.88.1:8728", nil) if err != nil { log.Fatalf("Failed to connect: %v", err) } defer client.Close() // Send command asynchronously ch, err := client.RunArgsChan(ctx, "/interface/print") if err != nil { log.Fatalf("Failed to send command: %v", err) } // Process replies from the channel for reply := range ch { if reply.Data != nil { for _, item := range reply.Data { fmt.Printf("Interface: %s\n", item["name"]) } } if reply.Err != nil { log.Printf("Error processing reply: %v\n", reply.Err) } } } ``` -------------------------------- ### Install RouterOS Client for Go Source: https://github.com/go-routeros/routeros/blob/master/README.md Use this command to install the latest version of the RouterOS client library for Go. ```bash go get github.com/go-routeros/routeros/v3 ``` -------------------------------- ### Secure Credential Storage Example Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/authentication.md Illustrates using a hypothetical secrets management library to load credentials. This is a best practice for production applications. ```go // Example using a hypothetical secrets library secrets := secretsmgr.LoadSecret("routeros/credentials") client, err := routeros.Dial( secrets.Get("host"), secrets.Get("username"), secrets.Get("password"), ) ``` -------------------------------- ### Example Reply String Output Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/reply.md Demonstrates the expected string output when calling the String() method on a Reply object. ```go reply, _ := client.Run("/interface/print") fmt.Println(reply.String()) // Output: // !re @r1 [["name" "ether1"] ["type" "ether"] ...] // !done @r1 [] ``` -------------------------------- ### Get and Set System Identity Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/commands.md Demonstrates how to retrieve the current system identity and how to set a new identity for the router. The get operation checks for a response before printing. ```go // Get identity reply, err := client.Run("/system/identity/print") if len(reply.Re) > 0 { fmt.Println("Identity:", reply.Re[0].Map["name"]) } // Set identity client.Run("/system/identity/set", "=name=MyRouter") ``` -------------------------------- ### List Installed Packages Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/commands.md Fetches and displays a list of all installed packages on the RouterOS system, including their names and versions. Error handling is included for the command execution. ```go reply, err := client.Run("/system/package/print") if err != nil { log.Fatal(err) } for _, pkg := range reply.Re { fmt.Printf("%s (version %s)\n", pkg.Map["name"], pkg.Map["version"], ) } ``` -------------------------------- ### Minimal RouterOS Client Connection and Command Execution Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/INDEX.md This example demonstrates establishing a connection to a RouterOS device, executing a '/interface/print' command, and iterating over the results to print interface names. Ensure you replace '192.168.1.1:8728', 'admin', and 'password' with your actual device credentials and address. ```go package main import ( "log" "github.com/go-routeros/routeros/v3" ) func main() { client, err := routeros.Dial("192.168.1.1:8728", "admin", "password") if err != nil { log.Fatal(err) } defer client.Close() reply, err := client.Run("/interface/print") if err != nil { log.Fatal(err) } for _, sentence := range reply.Re { println(sentence.Map["name"]) } } ``` -------------------------------- ### Two-Stage Authentication (Pre-RouterOS 6.43) Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/authentication.md This example demonstrates connecting using `DialContext` which automatically handles the challenge-response mechanism for older RouterOS versions. ```go client, err := routeros.DialContext(ctx, "192.168.1.1:8728", "admin", "password") // Library detects pre-6.43 and handles challenge-response automatically ``` -------------------------------- ### Credential Management using Configuration Files Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/authentication.md This example shows how to load RouterOS connection details from a JSON configuration file. It supports both plain text and TLS connections based on the configuration. ```go type Config struct { Host string Port int Username string Password string UseTLS bool } func LoadConfig(filePath string) (*Config, error) { data, _ := ioutil.ReadFile(filePath) var cfg Config if err := json.Unmarshal(data, &cfg); err != nil { return nil, err } return &cfg, nil } cfg, _ := LoadConfig("routeros.json") var client *routeros.Client if cfg.UseTLS { client, _ = routeros.DialTLS( fmt.Sprintf("%s:%d", cfg.Host, cfg.Port), cfg.Username, cfg.Password, nil, ) } else { client, _ = routeros.Dial( fmt.Sprintf("%s:%d", cfg.Host, cfg.Port), cfg.Username, cfg.Password, ) } ``` -------------------------------- ### Word Decoding Example (Reading) Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/advanced.md Demonstrates how to read and decode a length-prefixed word from the device. It involves reading the length byte(s) and then reading the specified number of data bytes. ```go // Reading: // 1. Read length byte(s): 0x05 // 2. Decode: length = 5 // 3. Read 5 bytes: "hello" ``` -------------------------------- ### Configure Client Logging with a Handler Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/types.md Demonstrates how to set a custom log handler for the client using the SetLogHandler method. This example configures a JSON handler that outputs debug-level logs to standard output. ```go handler := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ Level: slog.LevelDebug, }) client.SetLogHandler(handler) ``` -------------------------------- ### One-Stage Authentication (RouterOS 6.43+) Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/authentication.md This example shows connecting using `DialContext` for newer RouterOS versions where credentials are sent directly without a challenge. ```go client, err := routeros.DialContext(ctx, "192.168.1.1:8728", "admin", "password") // Library detects post-6.43 and sends credentials directly ``` -------------------------------- ### Handle TLS Certificate Verification Failures Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/authentication.md Example of attempting a TLS connection and checking for common verification errors. Includes common causes for failure. ```go _, err := routeros.DialTLS("192.168.1.1:8729", "admin", "password", &tls.Config{}) if err != nil { // Common causes: // 1. Self-signed certificate (add to CA pool) // 2. Certificate expired // 3. Hostname mismatch // 4. Wrong CA certificate fmt.Println("TLS error:", err) } ``` -------------------------------- ### Enable and Disable Interfaces Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/commands.md Provides examples for enabling, disabling, and enabling multiple network interfaces by their numbers. These commands directly modify the interface state. ```go // Enable interface client.Run("/interface/enable", "=numbers=ether1") // Disable interface client.Run("/interface/disable", "=numbers=ether1") // Enable multiple client.Run("/interface/enable", "=numbers=ether1,ether2,ether3") ``` -------------------------------- ### Get Resource Information Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/patterns.md Retrieves system resource information, including board name, CPU, total memory, and uptime. Assumes at least one resource entry is returned. ```go reply, err := client.Run("/system/resource/print") if err != nil { log.Fatal(err) } if len(reply.Re) > 0 { res := reply.Re[0] fmt.Printf("Board: %s\n", res.Map["board-name"]) fmt.Printf("CPU: %s\n", res.Map["cpu"]) fmt.Printf("Memory: %s bytes\n", res.Map["total-memory"]) fmt.Printf("Uptime: %s\n", res.Map["uptime"]) } ``` -------------------------------- ### Start Async Mode Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/async.md Enables asynchronous mode for the client. Returns a channel to monitor for errors in the async loop. This method should only be called once per client instance. ```go func (c *Client) Async() <-chan error ``` -------------------------------- ### Word Encoding Example (Writing) Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/advanced.md Illustrates the process of encoding a word for transmission, including calculating its length and writing the length-prefixed bytes. This is fundamental for protocol communication. ```go // Writing "hello": // 1. Length = 5 (0x05) // 2. Encode length: 0x05 (5 < 128) // 3. Write bytes: 05 68 65 6c 6c 6f ``` -------------------------------- ### Start Async Mode with Context Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/async.md Enables asynchronous mode with context support. The provided context controls the async loop; cancellation will stop the loop. Returns a channel for error monitoring. ```go func (c *Client) AsyncContext(ctx context.Context) <-chan error ``` -------------------------------- ### Get System Resource Information Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/commands.md Retrieves detailed system resource information such as board name, CPU, memory, and uptime. Requires checking the response for the presence of data before accessing fields. ```go reply, err := client.Run("/system/resource/print") if err != nil { log.Fatal(err) } if len(reply.Re) > 0 { res := reply.Re[0].Map fmt.Printf("Board: %s\n", res["board-name"]) fmt.Printf("CPU: %s\n", res["cpu"]) fmt.Printf("Memory: %s\n", res["total-memory"]) fmt.Printf("Uptime: %s\n", res["uptime"]) } ``` -------------------------------- ### Implement a Custom Log Handler Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/advanced.md Create a custom log handler by embedding a standard handler and adding custom filtering logic. This example filters to only show ERROR and WARN level messages. ```go type CustomHandler struct { slog.Handler filter func(string) bool } client.SetLogHandler(&CustomHandler{ Handler: slog.NewTextHandler(os.Stdout, nil), filter: func(level string) bool { return level == "ERROR" || level == "WARN" // Only errors/warnings }, }) ``` -------------------------------- ### Proto: Sentence String Representation Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/MANIFEST.txt Method to get the string representation of a Sentence object. ```APIDOC ## Proto: Sentence String Representation ### Description Method to get the string representation of a Sentence object. ### Method - **String() string**: Returns the Sentence as a formatted string. ``` -------------------------------- ### Read Sentence from Connection Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/proto.md Demonstrates creating a network connection, initializing a proto.Reader, and reading the first sentence, handling potential errors. ```go conn, _ := net.Dial("tcp", "192.168.1.1:8728") reader := proto.NewReader(conn) sentence, err := reader.ReadSentence() if err != nil { log.Fatal(err) } fmt.Println(sentence.Word) ``` -------------------------------- ### Establish Subscription with Context Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/listen.md Use ListenContext to establish a subscription with context support. This is useful for managing the lifecycle of the subscription with a context. ```go ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() listen, err := client.ListenContext(ctx, "/ip/firewall/address-list/listen") if err != nil { log.Fatal(err) } ``` -------------------------------- ### Creating and Using a RouterOS Writer Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/types.md Demonstrates the fundamental steps to create a Writer and send a simple command like '/interface/print' to a RouterOS device. ```go writer := proto.NewWriter(conn) writer.BeginSentence() writer.WriteWord("/interface/print") writer.EndSentence() ``` -------------------------------- ### Manual Authentication with NewClient Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/authentication.md Use this method when you need to create a client from an existing network connection. It involves manually establishing the connection, creating the client, and then authenticating. ```go // Create connection manually conn, err := net.Dial("tcp", "192.168.1.1:8728") if err != nil { log.Fatal(err) } // Create client client, err := routeros.NewClient(conn) if err != nil { log.Fatal(err) } // Authenticate err = client.Login("admin", "password") if err != nil { log.Fatal(err) conn.Close() } deffer client.Close() ``` -------------------------------- ### Update IP Address Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/commands.md Modifies an existing IP address entry, identified by its ID. This example shows how to disable an IP address. ```go client.Run( "/ip/address/set", "=.id=1", "=disabled=true", ) ``` -------------------------------- ### Create and Use Service Account for Automation Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/authentication.md Shows how to create a dedicated service account with write permissions and use it for automated tasks. ```go // Create a dedicated service account client, _ := routeros.Dial("192.168.1.1:8728", "admin", "admin_pass") client.Run( "/user/add", "=name=automation", "=password=secure_service_password", "=group=write", ) // Use the service account for automated tasks automation, _ := routeros.Dial("192.168.1.1:8728", "automation", "secure_service_password") // All operations use this account reply, _ := automation.Run("/interface/print") ``` -------------------------------- ### Add User Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/commands.md Creates a new user account with a specified name, password, and group. ```go client.Run( "/user/add", "=name=newuser", "=password=securepass", "=group=read", ) ``` -------------------------------- ### Enable Async Mode and Monitor Errors Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/async.md Demonstrates enabling asynchronous mode and setting up a goroutine to monitor for errors. Commands issued after enabling async mode return immediately. ```go client, _ := routeros.Dial("192.168.1.1:8728", "admin", "password") errC := client.Async() if errC == nil { log.Fatal("Failed to enable async mode") } // Now commands return immediately reply, err := client.Run("/interface/print") if err != nil { log.Fatal(err) } // Monitor for async errors in a goroutine go func() { if err := <-errC; err != nil { log.Printf("Async loop error: %v", err) } }() ``` -------------------------------- ### Client.ListenContext Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/listen.md Establishes a subscription with context support using the Client's Queue size. This method allows for cancellation of the subscription via a provided context. ```APIDOC ## Method: ListenContext ### Description Establishes a subscription with context support using the Client's Queue size. This method allows for cancellation of the subscription via a provided context. ### Signature ```go func (c *Client) ListenContext(ctx context.Context, sentence ...string) (*ListenReply, error) ``` ### Parameters #### Path Parameters - **ctx** (context.Context) - Required - Context controlling the operation. - **sentence** (...string) - Required - Command words as variadic arguments. ### Returns - `*ListenReply`: A subscription object. - `error`: Error if subscription or context cancellation occurs. ### Example ```go ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() listen, err := client.ListenContext(ctx, "/ip/firewall/address-list/listen") if err != nil { log.Fatal(err) } ``` ``` -------------------------------- ### Establish Subscription with Sentence Slice and Context Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/listen.md Use ListenArgsContext to establish a subscription with both a sentence slice and context support. This provides flexibility in command formatting and lifecycle management. ```go ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() command := []string{"/ip/firewall/address-list/listen"} listen, err := client.ListenArgsContext(ctx, command) if err != nil { log.Fatal(err) } ``` -------------------------------- ### Client.AsyncContext Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/async.md Starts asynchronous mode with context support. This method allows the asynchronous loop to be controlled by a `context.Context`, enabling cancellation of the loop and interruption of pending operations. ```APIDOC ## Method: AsyncContext Starts asynchronous mode with context support. ### Signature ```go func (c *Client) AsyncContext(ctx context.Context) <-chan error ``` ### Parameters #### Path Parameters - **ctx** (context.Context) - Required - Context controlling the async loop. Cancellation stops the loop. ### Returns - `<-chan error`: A receive-only channel yielding errors or nil. Closes when the context is cancelled or the Client is closed. ### Throws | Error | |-------| | errAlreadyAsync | Async mode is already enabled. | ### Notes - When the context is cancelled, the async loop stops and all pending operations are interrupted. - The error channel closes after the context cancellation completes. ### Example ```go ctx, cancel := context.WithCancel(context.Background()) client, _ := routeros.Dial("192.168.1.1:8728", "admin", "password") errC := client.AsyncContext(ctx) go func() { // Cancel async mode after 1 minute time.Sleep(1 * time.Minute) cancel() }() // Use the client in async mode... // Wait for async loop to finish if err := <-errC; err != nil { log.Printf("Async error: %v", err) } ``` ``` -------------------------------- ### Establish Subscription with Sentence Slice Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/listen.md Use ListenArgs to establish a subscription using a slice of strings for the command. This method is suitable when commands are already prepared as a slice. ```go client, _ := routeros.Dial("192.168.1.1:8728", "admin", "password") client.Queue = 50 command := []string{"/interface/listen"} listen, err := client.ListenArgs(command) if err != nil { log.Fatal(err) } ``` -------------------------------- ### Writer Interface Definition Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/proto.md Defines the interface for writing sentences to a RouterOS device connection, including methods for starting, writing words, ending sentences, canceling, and closing. ```go type Writer interface { BeginSentence() WriteWord(word string) EndSentence() error Cancel() Close() } ``` -------------------------------- ### Populate and Use Sentence Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/proto.md Demonstrates how to create a new sentence, set its properties (Word, Tag), and add a key-value pair to both the List and Map. ```go sen := proto.NewSentence() sen.Word = "!re" sen.Tag = "r1" sen.List = append(sen.List, proto.Pair{Key: "name", Value: "ether1"}) sen.Map["name"] = "ether1" ``` -------------------------------- ### Implement Connection Pooling Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/authentication.md Use connection pooling to manage concurrent API connections efficiently, especially for high-volume operations. This example demonstrates acquiring and releasing clients from a pool. ```go // Use connection pooling for high-volume operations pool := NewClientPool("192.168.1.1:8728", "admin", "password", 5) for i := 0; i < 100; i++ { c, _ := pool.Acquire() go func(client *routeros.Client) { client.Run("/interface/print") pool.Release(client) }(c) } ``` -------------------------------- ### NewClient Constructor Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/client.md Initializes a new Client from an existing network connection. The client is not yet authenticated and requires a subsequent Login call. The Client takes ownership of the connection. ```go conn, _ := net.Dial("tcp", "192.168.1.1:8728") client, err := routeros.NewClient(conn) if err != nil { log.Fatal(err) } deferr client.Close() err = client.Login("admin", "password") if err != nil { log.Fatal(err) } ``` -------------------------------- ### Connection with Context Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/patterns.md Establishes a connection using a context, allowing for cancellation and timeouts. Ensure the context is properly managed with `defer cancel()`. ```go ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) deffer cancel() client, err := routeros.DialContext(ctx, "192.168.1.1:8728", "admin", "password") if err != nil { log.Fatal(err) } deffer client.Close() ``` -------------------------------- ### Create and Use Read-Only User Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/authentication.md Demonstrates creating a read-only user and connecting with limited permissions. Write operations will fail. ```go client, _ := routeros.Dial("192.168.1.1:8728", "admin", "admin_password") client.Run( "/user/add", "=name=monitor", "=password=monitor_pass", "=group=read", // Read-only group ) // Later, connect with limited user monitor, _ := routeros.Dial("192.168.1.1:8728", "monitor", "monitor_pass") // This succeeds (read operation) reply, _ := monitor.Run("/interface/print") // This fails (write operation) _, err := monitor.Run("/interface/set", "=.id=ether1", "=disabled=true") if err != nil { fmt.Println("Permission denied:", err) } ``` -------------------------------- ### Client.Async Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/async.md Starts asynchronous mode for the client. This method returns a channel that can be used to receive errors from the background asynchronous loop. It's crucial to monitor this channel for connection failures or other issues. ```APIDOC ## Method: Async Starts asynchronous mode and returns a channel for receiving async loop errors. ### Signature ```go func (c *Client) Async() <-chan error ``` ### Returns - `<-chan error`: A receive-only channel that yields errors from the async loop, or nil if no error occurs. The channel closes when the Client is closed or the async loop exits. ### Throws | Error | |-------| | errAlreadyAsync | Async() has already been called on this Client. | ### Notes - In asynchronous mode, commands (Run, Listen) return immediately after sending the request. - Replies are assembled in the background and delivered via channels. - The error channel must be monitored to detect connection failures. - Calling Async() multiple times on the same Client returns an error. - Once enabled, asynchronous mode cannot be disabled for that Client instance. ### Example ```go client, _ := routeros.Dial("192.168.1.1:8728", "admin", "password") errC := client.Async() if errC == nil { log.Fatal("Failed to enable async mode") } // Now commands return immediately reply, err := client.Run("/interface/print") if err != nil { log.Fatal(err) } // Monitor for async errors in a goroutine go func() { if err := <-errC; err != nil { log.Printf("Async loop error: %v", err) } }() ``` ``` -------------------------------- ### NewWriter Constructor Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/MANIFEST.txt Demonstrates the creation of a new writer for protocol encoding. ```go w := proto.NewWriter(conn) ``` -------------------------------- ### Extracting Details from DeviceError in Go Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/errors.md Shows how to access specific fields like 'message' and 'category' from a DeviceError's Sentence to get detailed information about the error. Useful for diagnosing command failures. ```go reply, err := client.Run("/ip/address/add", "=address=invalid") if devErr, ok := err.(*routeros.DeviceError); ok { sentence := devErr.Sentence message := sentence.Map["message"] category := sentence.Map["category"] fmt.Printf("Error message: %s\n", message) fmt.Printf("Error category: %s\n", category) fmt.Printf("Word type: %s\n", sentence.Word) // "!trap" or "!fatal" } ``` -------------------------------- ### Establish RouterOS Subscription with Client Queue Size Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/listen.md Shows how to initiate a RouterOS subscription using the client's configured queue size for event buffering. This method is a convenience wrapper that also enables asynchronous mode. ```go client, _ := routeros.Dial("192.168.1.1:8728", "admin", "password") client.Queue = 100 listen, err := client.Listen("/interface/listen") if err != nil { log.Fatal(err) } for sentence := range listen.Chan() { fmt.Printf("Interface update: %+v\n", sentence.Map) } ``` -------------------------------- ### Configuring Buffered Queues for Listeners Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/patterns.md Details how to set a buffer size for the listener queue before starting to listen for events. A buffered queue prevents message loss if events are generated faster than they can be processed. ```go client.Queue = 100 // Set before calling Listen listen, _ := client.Listen("/interface/listen") ``` -------------------------------- ### Manual Client Creation Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/INDEX.md Use NewClient to create a Client instance from an existing io.ReadWriteCloser. This is useful for custom connection types or when managing the underlying transport manually. ```go NewClient(rwc io.ReadWriteCloser) *Client ``` -------------------------------- ### Connect and Query Interfaces Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/INDEX.md Establishes a connection to a RouterOS device and retrieves a list of network interfaces. ```go client, _ := routeros.Dial("192.168.1.1:8728", "admin", "password") reply, _ := client.Run("/interface/print") for _, iface := range reply.Re { println(iface.Map["name"]) } ``` -------------------------------- ### Using Reply in Asynchronous Mode Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/reply.md Demonstrates how the Reply object is populated correctly even when commands are run in asynchronous mode. The reply is gradually populated as sentences arrive. ```go client, _ := routeros.Dial("192.168.1.1:8728", "admin", "password") client.Async() reply, err := client.Run("/interface/print") if err != nil { log.Fatal(err) } // Reply is gradually populated as sentences arrive // Access data after reading from channels or after returning for _, iface := range reply.Re { fmt.Println(iface.Map["name"]) } ``` -------------------------------- ### Connection Reuse for Performance Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/advanced.md Demonstrates establishing a connection once and reusing it for multiple operations to improve performance, as shown in the loop making 1000 calls. ```go client, _ := routeros.Dial(...) defuncient.Close() for i := 0; i < 1000; i++ { client.Run(...) // Reuses same connection } ``` -------------------------------- ### Establish Subscription with Queue Size and Context Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/listen.md Use ListenArgsQueueContext to establish a subscription with explicit queue size and context support. This method automatically enables asynchronous mode and assigns a unique tag. ```go ctx, cancel := context.WithCancel(context.Background()) listen, err := client.ListenArgsQueueContext(ctx, []string{"/interface/listen"}, 100) if err != nil { log.Fatal(err) } go func() { time.Sleep(30 * time.Second) cancel() // Stop listening after 30 seconds }() for sentence := range listen.Chan() { fmt.Printf("Interface changed: %+v\n", sentence.Map) } ``` -------------------------------- ### List Simple Queues Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/commands.md Retrieves and prints details of simple queue configurations, including their name and target. ```go reply, err := client.Run("/queue/simple/print") if err != nil { log.Fatal(err) } for _, queue := range reply.Re { fmt.Printf("Queue: %s (%s)\n", queue.Map["name"], queue.Map["target"], ) } ``` -------------------------------- ### Handle Context-Based Timeout Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/errors.md Demonstrates setting a timeout for a command using context.WithTimeout and handling the DeadlineExceeded error specifically. ```go ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() reply, err := client.RunContext(ctx, "/system/resource/print") if err == context.DeadlineExceeded { fmt.Println("Command took too long") } else if err != nil { fmt.Printf("Error: %v\n", err) } ``` -------------------------------- ### Listen for Updates in Async Mode Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/async.md Shows how to use a Listen method, which automatically enables asynchronous mode if not already active. Updates are received via a channel. ```go client, _ := routeros.Dial("192.168.1.1:8728", "admin", "password") // Listen implicitly calls Async() if needed listen, err := client.Listen("/interface/listen") if err != nil { log.Fatal(err) } // Update channel receives !re sentences for sentence := range listen.Chan() { fmt.Println("Interface updated:", sentence.Map) } ``` -------------------------------- ### Select Specific Interface Properties Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/commands.md Illustrates how to retrieve only a specified subset of properties (name, MTU, MAC address) for interface objects. ```go reply, _ := client.Run( "/interface/print", "=.proplist=name,mtu,mac-address", ) // Now reply.Re only contains these fields for _, iface := range reply.Re { fmt.Println(iface.Map["name"]) // iface.Map["mtu"] is available // iface.Map["running"] is NOT available (not in proplist) } ``` -------------------------------- ### Connection with Timeout Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/patterns.md Establishes a connection with a specified timeout for both connection and login. This prevents indefinite blocking. ```go client, err := routeros.DialTimeout( "192.168.1.1:8728", "admin", "password", 10*time.Second, // Timeout for connection and login ) if err != nil { log.Fatal(err) } deffer client.Close() ``` -------------------------------- ### Listen for Interface Changes Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/commands.md Sets up a listener to receive real-time notifications whenever changes occur to network interfaces. It continuously prints the details of detected changes. ```go listen, err := client.Listen("/interface/listen") if err != nil { log.Fatal(err) } for sentence := range listen.Chan() { fmt.Printf("Interface changed: %+v\n", sentence.Map) } ``` -------------------------------- ### Add Simple Queue Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/commands.md Creates a new simple queue entry, defining its name, target IP address, maximum bandwidth limits, and a comment. ```go client.Run( "/queue/simple/add", "=name=client1", "=target=192.168.1.100/32", "=max-limit=1M/1M", "=comment=Client bandwidth limit", ) ``` -------------------------------- ### Enable Async Mode with Context and Cancel Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/async.md Shows how to enable async mode using a context and cancel it after a delay. The async loop is stopped when the context is cancelled. ```go ctx, cancel := context.WithCancel(context.Background()) client, _ := routeros.Dial("192.168.1.1:8728", "admin", "password") errC := client.AsyncContext(ctx) go func() { // Cancel async mode after 1 minute time.Sleep(1 * time.Minute) cancel() }() // Use the client in async mode... // Wait for async loop to finish if err := <-errC; err != nil { log.Printf("Async error: %v", err) } ``` -------------------------------- ### Best Practices Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/MANIFEST.txt A summary of best practices for using the RouterOS client library effectively and reliably. ```APIDOC ## Best Practices ### Description This section consolidates best practices for utilizing the Go RouterOS client library, ensuring robust, efficient, and maintainable interactions with RouterOS devices. ### Connection Management - Proper handling of connection lifecycles (opening, closing). - Utilizing context for cancellation. ### Error Handling - Comprehensive checking and handling of all potential errors. - Differentiating between device errors and network errors. ### Resource Management - Ensuring resources (connections, goroutines) are properly cleaned up. ### Performance Considerations - Using batch operations where appropriate. - Avoiding unnecessary API calls. ``` -------------------------------- ### Configure Custom Log Level Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/patterns.md Set up text-based logging with the option to include source file information and a specific log level. This allows for detailed debugging when needed. ```go client, _ := routeros.Dial("192.168.1.1:8728", "admin", "password") handler := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{ AddSource: true, Level: slog.LevelDebug, }) client.SetLogHandler(handler) ``` -------------------------------- ### Client.ListenArgsContext Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/listen.md Establishes a subscription from a sentence slice with context support and Client's Queue size. This method combines context-based cancellation with slice-based command arguments. ```APIDOC ## Method: ListenArgsContext ### Description Establishes a subscription from a sentence slice with context support and Client's Queue size. This method combines context-based cancellation with slice-based command arguments. ### Signature ```go func (c *Client) ListenArgsContext(ctx context.Context, sentence []string) (*ListenReply, error) ``` ### Parameters #### Path Parameters - **ctx** (context.Context) - Required - Context controlling the operation. - **sentence** ([]string) - Required - Command words as a slice. ### Returns - `*ListenReply`: A subscription object. - `error`: Error if subscription or context cancellation occurs. ### Example ```go ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() command := []string{"/ip/firewall/address-list/listen"} listen, err := client.ListenArgsContext(ctx, command) if err != nil { log.Fatal(err) } ``` ``` -------------------------------- ### Client.ListenArgs Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/listen.md Establishes a subscription from a sentence slice using the Client's Queue size. This method is suitable for when commands are already prepared as a string slice. ```APIDOC ## Method: ListenArgs ### Description Establishes a subscription from a sentence slice using the Client's Queue size. This method is suitable for when commands are already prepared as a string slice. ### Signature ```go func (c *Client) ListenArgs(sentence []string) (*ListenReply, error) ``` ### Parameters #### Path Parameters - **sentence** ([]string) - Required - Command words as a slice. ### Returns - `*ListenReply`: A subscription object. - `error`: Error if subscription fails. ### Example ```go client, _ := routeros.Dial("192.168.1.1:8728", "admin", "password") client.Queue = 50 command := []string{"/interface/listen"} listen, err := client.ListenArgs(command) if err != nil { log.Fatal(err) } ``` ``` -------------------------------- ### List PPP Interfaces Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/commands.md Fetches and prints details of PPP interfaces, such as PPPoE clients, including their name, associated interface, and disabled status. ```go reply, err := client.Run("/interface/pppoe-client/print") if err != nil { log.Fatal(err) } for _, ppp := range reply.Re { fmt.Printf("PPPoE: %s on %s (disabled: %s)\n", ppp.Map["name"], ppp.Map["interface"], ppp.Map["disabled"], ) } ``` -------------------------------- ### Manual Authentication Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/INDEX.md Use Login to manually authenticate the client with a username and password. This is an alternative to providing credentials during connection. ```go Login(user, pass string) error ``` -------------------------------- ### Credential Management using Environment Variables Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/authentication.md This snippet demonstrates how to securely load username and password from environment variables. It provides a default username if the variable is not set. ```go username := os.Getenv("ROUTEROS_USER") if username == "" { username = "admin" } password := os.Getenv("ROUTEROS_PASSWORD") if password == "" { log.Fatal("ROUTEROS_PASSWORD environment variable not set") } client, err := routeros.Dial("192.168.1.1:8728", username, password) ``` -------------------------------- ### Use Multiple Contexts for Commands Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/advanced.md Each command can be executed with its own context, allowing for different timeouts or cancellation signals for concurrent operations. ```go // Each command can have different context ctx1, _ := context.WithTimeout(context.Background(), 5*time.Second) ctx2, _ := context.WithTimeout(context.Background(), 10*time.Second) reply1, _ := client.RunContext(ctx1, "/quick/command") reply2, _ := client.RunContext(ctx2, "/slow/command") ``` -------------------------------- ### Simple Query for System Resource Information Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/reply.md Executes a simple query to retrieve system resource information and accesses specific fields like board name and total memory. ```go client, _ := routeros.Dial("192.168.1.1:8728", "admin", "password") reply, err := client.Run("/system/resource/print") if err != nil { log.Fatal(err) } // Access resource information if len(reply.Re) > 0 { res := reply.Re[0] fmt.Printf("Board name: %s\n", res.Map["board-name"]) fmt.Printf("Memory: %s\n", res.Map["total-memory"]) } ``` -------------------------------- ### Subscribe to Events with All Options Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/INDEX.md Use ListenArgsQueueContext to subscribe to events with both context and queue size support. This offers the most control over event subscriptions. ```go ListenArgsQueueContext(ctx context.Context, sentences []string, queueSize int) (*ListenReply, error) ``` -------------------------------- ### List IP Routes Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/commands.md Fetches and displays all configured IP routes, showing the destination address and the gateway used for routing. Includes error handling. ```go reply, err := client.Run("/ip/route/print") if err != nil { log.Fatal(err) } for _, route := range reply.Re { fmt.Printf("%s via %s\n", route.Map["dst-address"], route.Map["gateway"], ) } ``` -------------------------------- ### Client.ListenArgsQueueContext Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/listen.md Establishes a subscription with explicit queue size and context support. This method offers the most control, allowing for context-based cancellation and a specified buffer size. ```APIDOC ## Method: ListenArgsQueueContext ### Description Establishes a subscription with explicit queue size and context support. This method offers the most control, allowing for context-based cancellation and a specified buffer size. ### Signature ```go func (c *Client) ListenArgsQueueContext(ctx context.Context, sentence []string, queueSize int) (*ListenReply, error) ``` ### Parameters #### Path Parameters - **ctx** (context.Context) - Required - Context controlling the operation. - **sentence** ([]string) - Required - Command words as a slice. - **queueSize** (int) - Required - Buffer size for the update channel. ### Returns - `*ListenReply`: A subscription object. - `error`: Error if subscription or context cancellation occurs. ### Throws #### Error Handling - **DeviceError**: RouterOS device returned !trap or !fatal. - **Network error**: Connection lost. ### Notes - Automatically enables asynchronous mode if not already active. - Assigns a unique tag to the subscription for routing updates. - Context cancellation stops the subscription and closes the channel. ### Example ```go ctx, cancel := context.WithCancel(context.Background()) listen, err := client.ListenArgsQueueContext(ctx, []string{"/interface/listen"}, 100) if err != nil { log.Fatal(err) } go func() { time.Sleep(30 * time.Second) cancel() // Stop listening after 30 seconds }() for sentence := range listen.Chan() { fmt.Printf("Interface changed: %+v\n", sentence.Map) } ``` ``` -------------------------------- ### Configure JSON Logging Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/patterns.md Set up the client to log messages in JSON format to standard output. This is beneficial for structured logging and integration with log aggregation systems. ```go client, _ := routeros.Dial("192.168.1.1:8728", "admin", "password") handler := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ Level: slog.LevelDebug, }) client.SetLogHandler(handler) reply, _ := client.Run("/interface/print") ``` -------------------------------- ### Listen for Real-time Events Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/INDEX.md Sets up a listener to receive real-time event updates from the RouterOS device. ```go listen, _ := client.Listen("/interface/listen") for sentence := range listen.Chan() { println(sentence.Map) } ``` -------------------------------- ### Handle Read/Write Errors Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/errors.md Illustrates how to handle read/write errors from Run*() and Listen*() methods, checking for specific conditions like EOF or timeouts. ```go reply, err := client.Run("/interface/print") if err != nil { if errors.Is(err, io.EOF) { fmt.Println("Connection closed") } // or if err, ok := err.(net.Error); ok && err.Timeout() { fmt.Println("Operation timed out") } } ``` -------------------------------- ### Print Sentence String Representation Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/proto.md Shows how to obtain and print the string representation of a Sentence, including its word, tag, and key-value pairs. ```go sen := proto.NewSentence() sen.Word = "!re" sen.Tag = "r1" sen.Map["name"] = "ether1" fmt.Println(sen.String()) // Output: !re @r1 [["name" "ether1"]] ``` -------------------------------- ### Creating and Using a RouterOS Reader Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/types.md Illustrates the basic usage of the proto.NewReader function to create a Reader instance and read a single sentence from a connection. ```go reader := proto.NewReader(conn) sentence, err := reader.ReadSentence() ``` -------------------------------- ### Writer.BeginSentence Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/proto.md Prepares the Writer to write a new sentence. This must be called before writing any words for a sentence and acquires a lock to prevent concurrent writes. ```APIDOC ### Method: BeginSentence Prepares the Writer to write a new sentence. ```go func (w Writer) BeginSentence() ``` **Notes:** - Must be called before writing words for a sentence. - Acquires a lock to prevent concurrent writes. ``` -------------------------------- ### Manual Authentication with NewClient Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/MANIFEST.txt Guidance on performing authentication manually when using the `NewClient` function. ```APIDOC ## Manual Authentication with NewClient ### Description This section provides instructions on how to manually handle the authentication process when using the `NewClient` function, offering flexibility in managing credentials and security steps. ### Steps 1. Create a client instance using `NewClient`. 2. Manually initiate the authentication sequence (e.g., challenge-response). 3. Send login credentials. 4. Handle the authentication result. ### Use Cases - Custom authentication flows. - Integrating with external credential management systems. ``` -------------------------------- ### Setting Command Timeouts with Context Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/patterns.md Shows how to set a timeout for RouterOS commands using `context.WithTimeout`. This prevents commands from hanging indefinitely and ensures timely responses. ```go ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) deferr cancel() reply, _ := client.RunContext(ctx, "/interface/print") ``` -------------------------------- ### Adding an IP Address with Parameters Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/reply.md Adds a new IP address to a specified interface using command parameters and retrieves the ID of the newly created address. ```go reply, _ := client.Run( "/ip/address/add", "=address=192.168.88.2/24", "=interface=ether2", ) if len(reply.Re) > 0 { resultId := reply.Re[0].Map[".id"] fmt.Printf("Created address with ID: %s\n", resultId) } ``` -------------------------------- ### Issue Multiple Commands in Async Mode Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/async.md Demonstrates issuing multiple commands concurrently in asynchronous mode. Commands return immediately, and replies are processed in the background. ```go client, _ := routeros.Dial("192.168.1.1:8728", "admin", "password") errC := client.Async() // Multiple commands can be issued without blocking reply1, err := client.Run("/interface/print") reply2, err := client.Run("/ip/address/print") reply3, err := client.Run("/system/resource/print") // Replies are assembled in the background go func() { if err := <-errC; err != nil { log.Fatal(err) } }() ``` -------------------------------- ### Connection Patterns (TCP, TLS, timeout, context) Source: https://github.com/go-routeros/routeros/blob/master/_autodocs/MANIFEST.txt Details on establishing and managing connections using various patterns including TCP, TLS, timeouts, and context cancellation. ```APIDOC ## Connection Patterns ### Description This section covers various strategies for establishing and managing network connections to RouterOS devices, including support for TCP and TLS, handling timeouts, and integrating with Go's context for cancellation. ### TCP Connections - Establishing a basic TCP connection. - Handling connection errors. ### TLS Connections - Configuring and establishing secure TLS connections. - Custom certificate verification. ### Timeout Handling - Setting connection and read/write timeouts. - Using `context.Context` for request-level timeouts. ### Context Integration - Using `context.Context` to manage request lifecycles and cancellations. - Propagating context through API calls. ```