Skip to content

Commit 09c6eb3

Browse files
committed
first commit
1 parent 51fe300 commit 09c6eb3

File tree

8 files changed

+673
-1
lines changed

8 files changed

+673
-1
lines changed

README.md

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,95 @@
1-
# go-sip
1+
# Go-SIP
2+
3+
A simple SIP server implemented in Go.
4+
5+
## Features
6+
7+
- Reception and processing of SIP messages
8+
- Handling of REGISTER, INVITE, and BYE requests
9+
- Generation of SIP responses
10+
- Configuration via config file
11+
12+
## Usage
13+
14+
### Starting the Server
15+
16+
Start the server with the following command:
17+
18+
```
19+
go run main.go
20+
```
21+
22+
By default, the server listens on UDP port 5060.
23+
24+
### Configuration File
25+
26+
The server reads a configuration file named `config.json` by default. You can specify a different configuration file:
27+
28+
```
29+
go run main.go -config myconfig.json
30+
```
31+
32+
To generate a default configuration file:
33+
34+
```
35+
go run main.go -generate-config
36+
```
37+
38+
Example configuration file:
39+
40+
```json
41+
{
42+
"server": {
43+
"port": "5060",
44+
"log_level": "info",
45+
"bind_addr": "0.0.0.0"
46+
}
47+
}
48+
```
49+
50+
### Command Line Options
51+
52+
Override configuration file values with command line options:
53+
54+
```
55+
go run main.go -port 5080 -bind 127.0.0.1
56+
```
57+
58+
All options:
59+
60+
- `-config <file>` - Path to configuration file
61+
- `-generate-config` - Generate default config file and exit
62+
- `-port <port>` - Override port number
63+
- `-bind <addr>` - Override bind address
64+
65+
## Test Client
66+
67+
A simple SIP client is included for testing:
68+
69+
```
70+
go run examples/sip_client.go
71+
```
72+
73+
The client supports the following commands:
74+
75+
- register: Register a user
76+
- invite: Initiate a call
77+
- bye: End a call
78+
- exit: Exit the client
79+
80+
You can specify the server address and username:
81+
82+
```
83+
go run examples/sip_client.go -server 127.0.0.1:5060 -user alice
84+
```
85+
86+
## Supported SIP Methods
87+
88+
- REGISTER: User registration
89+
- INVITE: Call initiation
90+
- BYE: Call termination
91+
- ACK: Acknowledgment handling
92+
93+
## Disclaimer
94+
95+
This is a simple SIP server for demonstration purposes and is not recommended for production use. A real SIP server requires additional features such as authentication, security, and full SIP specification support.

config.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"server": {
3+
"port": "5060",
4+
"log_level": "info",
5+
"bind_addr": "0.0.0.0"
6+
}
7+
}

config/config.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package config
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"os"
7+
)
8+
9+
// Config represents the SIP server configuration
10+
type Config struct {
11+
Server ServerConfig `json:"server"`
12+
}
13+
14+
// ServerConfig holds server-specific settings
15+
type ServerConfig struct {
16+
Port string `json:"port"`
17+
LogLevel string `json:"log_level"`
18+
BindAddr string `json:"bind_addr"`
19+
}
20+
21+
// DefaultConfig returns the default configuration
22+
func DefaultConfig() *Config {
23+
return &Config{
24+
Server: ServerConfig{
25+
Port: "5060",
26+
LogLevel: "info",
27+
BindAddr: "0.0.0.0",
28+
},
29+
}
30+
}
31+
32+
// LoadConfig loads configuration from the specified path
33+
func LoadConfig(path string) (*Config, error) {
34+
// Load default configuration
35+
cfg := DefaultConfig()
36+
37+
// Check if file exists
38+
if _, err := os.Stat(path); os.IsNotExist(err) {
39+
return cfg, fmt.Errorf("config file not found: %s", path)
40+
}
41+
42+
// Read file
43+
configFile, err := os.ReadFile(path)
44+
if err != nil {
45+
return cfg, fmt.Errorf("error reading config file: %v", err)
46+
}
47+
48+
// Parse JSON
49+
if err := json.Unmarshal(configFile, cfg); err != nil {
50+
return cfg, fmt.Errorf("error parsing config file: %v", err)
51+
}
52+
53+
return cfg, nil
54+
}
55+
56+
// SaveConfig saves configuration to a file
57+
func SaveConfig(cfg *Config, path string) error {
58+
// Convert to JSON
59+
configJSON, err := json.MarshalIndent(cfg, "", " ")
60+
if err != nil {
61+
return fmt.Errorf("error encoding config to JSON: %v", err)
62+
}
63+
64+
// Write to file
65+
if err := os.WriteFile(path, configJSON, 0644); err != nil {
66+
return fmt.Errorf("error writing config file: %v", err)
67+
}
68+
69+
return nil
70+
}

examples/sip_client.go

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
package main
2+
3+
import (
4+
"bufio"
5+
"flag"
6+
"fmt"
7+
"log"
8+
"net"
9+
"os"
10+
"time"
11+
)
12+
13+
// Sample SIP client
14+
func main() {
15+
// Parse command line arguments
16+
serverAddr := flag.String("server", "127.0.0.1:5060", "SIP server address")
17+
username := flag.String("user", "user1", "SIP username")
18+
flag.Parse()
19+
20+
// Establish UDP connection
21+
addr, err := net.ResolveUDPAddr("udp", *serverAddr)
22+
if err != nil {
23+
log.Fatalf("Address resolution error: %v", err)
24+
}
25+
26+
conn, err := net.DialUDP("udp", nil, addr)
27+
if err != nil {
28+
log.Fatalf("UDP connection error: %v", err)
29+
}
30+
defer conn.Close()
31+
32+
fmt.Printf("SIP client started: %s\n", *username)
33+
fmt.Println("Commands: register, invite, bye, exit")
34+
35+
// Goroutine for receiving responses
36+
go func() {
37+
buffer := make([]byte, 65535)
38+
for {
39+
n, _, err := conn.ReadFromUDP(buffer)
40+
if err != nil {
41+
log.Printf("Response reading error: %v", err)
42+
continue
43+
}
44+
fmt.Printf("\nReceived response:\n%s\n", string(buffer[:n]))
45+
}
46+
}()
47+
48+
// Command input loop
49+
scanner := bufio.NewScanner(os.Stdin)
50+
for {
51+
fmt.Print("> ")
52+
if !scanner.Scan() {
53+
break
54+
}
55+
56+
cmd := scanner.Text()
57+
if cmd == "exit" {
58+
break
59+
}
60+
61+
switch cmd {
62+
case "register":
63+
sendRegister(conn, *username)
64+
case "invite":
65+
fmt.Print("Username to call: ")
66+
if !scanner.Scan() {
67+
break
68+
}
69+
callee := scanner.Text()
70+
sendInvite(conn, *username, callee)
71+
case "bye":
72+
sendBye(conn, *username)
73+
default:
74+
fmt.Println("Unknown command. Enter register, invite, bye, or exit.")
75+
}
76+
}
77+
}
78+
79+
func sendRegister(conn *net.UDPConn, username string) {
80+
callID := generateCallID()
81+
82+
msg := fmt.Sprintf("REGISTER sip:%s@localhost SIP/2.0\r\n"+
83+
"Via: SIP/2.0/UDP 127.0.0.1:5060;branch=z9hG4bK%s\r\n"+
84+
"From: <sip:%s@localhost>;tag=%s\r\n"+
85+
"To: <sip:%s@localhost>\r\n"+
86+
"Call-ID: %s\r\n"+
87+
"CSeq: 1 REGISTER\r\n"+
88+
"Contact: <sip:%[email protected]>\r\n"+
89+
"Max-Forwards: 70\r\n"+
90+
"User-Agent: Go-SIP-Client\r\n"+
91+
"Expires: 3600\r\n"+
92+
"Content-Length: 0\r\n\r\n",
93+
username, generateBranch(), username, generateTag(), username, callID, username)
94+
95+
_, err := conn.Write([]byte(msg))
96+
if err != nil {
97+
log.Printf("Message sending error: %v", err)
98+
return
99+
}
100+
101+
fmt.Println("REGISTER sent")
102+
}
103+
104+
func sendInvite(conn *net.UDPConn, caller, callee string) {
105+
callID := generateCallID()
106+
107+
sdp := fmt.Sprintf("v=0\r\n"+
108+
"o=%s 123456 654321 IN IP4 127.0.0.1\r\n"+
109+
"s=SIP Call\r\n"+
110+
"c=IN IP4 127.0.0.1\r\n"+
111+
"t=0 0\r\n"+
112+
"m=audio 49170 RTP/AVP 0\r\n"+
113+
"a=rtpmap:0 PCMU/8000\r\n", caller)
114+
115+
msg := fmt.Sprintf("INVITE sip:%s@localhost SIP/2.0\r\n"+
116+
"Via: SIP/2.0/UDP 127.0.0.1:5060;branch=z9hG4bK%s\r\n"+
117+
"From: <sip:%s@localhost>;tag=%s\r\n"+
118+
"To: <sip:%s@localhost>\r\n"+
119+
"Call-ID: %s\r\n"+
120+
"CSeq: 1 INVITE\r\n"+
121+
"Contact: <sip:%[email protected]>\r\n"+
122+
"Content-Type: application/sdp\r\n"+
123+
"Content-Length: %d\r\n\r\n%s",
124+
callee, generateBranch(), caller, generateTag(), callee, callID, caller, len(sdp), sdp)
125+
126+
_, err := conn.Write([]byte(msg))
127+
if err != nil {
128+
log.Printf("Message sending error: %v", err)
129+
return
130+
}
131+
132+
fmt.Println("INVITE sent")
133+
}
134+
135+
func sendBye(conn *net.UDPConn, username string) {
136+
callID := generateCallID()
137+
138+
msg := fmt.Sprintf("BYE sip:server@localhost SIP/2.0\r\n"+
139+
"Via: SIP/2.0/UDP 127.0.0.1:5060;branch=z9hG4bK%s\r\n"+
140+
"From: <sip:%s@localhost>;tag=%s\r\n"+
141+
"To: <sip:server@localhost>;tag=as6f4bc61\r\n"+
142+
"Call-ID: %s\r\n"+
143+
"CSeq: 1 BYE\r\n"+
144+
"Content-Length: 0\r\n\r\n",
145+
generateBranch(), username, generateTag(), callID)
146+
147+
_, err := conn.Write([]byte(msg))
148+
if err != nil {
149+
log.Printf("Message sending error: %v", err)
150+
return
151+
}
152+
153+
fmt.Println("BYE sent")
154+
}
155+
156+
func generateCallID() string {
157+
return fmt.Sprintf("%d", time.Now().UnixNano())
158+
}
159+
160+
func generateTag() string {
161+
return fmt.Sprintf("%d", time.Now().UnixNano()%100000)
162+
}
163+
164+
func generateBranch() string {
165+
return fmt.Sprintf("%d", time.Now().UnixNano()%1000000)
166+
}

go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module github.com/user/go-sip
2+
3+
go 1.20

0 commit comments

Comments
 (0)