Browse Source

Standardised filter; Added examples

tags/v52
Wijnand 2 years ago
parent
commit
2315a54ab5
4 changed files with 135 additions and 34 deletions
  1. 80
    34
      filter.go
  2. 31
    0
      filter_test.go
  3. 1
    0
      table.go
  4. 23
    0
      table_test.go

+ 80
- 34
filter.go View File

@@ -148,58 +148,75 @@ func responseName(c int) string {
148 148
 
149 149
 // Filter implements the OpenSMTPD filter API
150 150
 type Filter struct {
151
+	// Connect callback
152
+	Connect func(*Session, *ConnectQuery) error
153
+
154
+	// HELO callback
155
+	HELO func(*Session, string) error
156
+
157
+	// MAIL FROM callback
158
+	MAIL func(*Session, string, string) error
159
+
160
+	// RCPT TO callback
161
+	RCPT func(*Session, string, string) error
162
+
163
+	// DATA callback
164
+	DATA func(*Session) error
165
+
166
+	// DataLine callback
167
+	DataLine func(*Session, string) error
168
+
169
+	// EOM (end of message) callback
170
+	EOM func(*Session, uint32) error
171
+
172
+	// Reset callback
173
+	Reset func(*Session) error
174
+
175
+	// Disconnect callback
176
+	Disconnect func(*Session) error
177
+
178
+	// Commit callback
179
+	Commit func(*Session) error
180
+
151 181
 	Name    string
152 182
 	Version uint32
153 183
 
154 184
 	c net.Conn
155 185
 	m *Message
156 186
 
157
-	hooks int
158
-	flags int
159
-	ready bool
160
-
161
-	hook struct {
162
-		connect    func(*Session, *ConnectQuery) error
163
-		helo       func(*Session, string) error
164
-		mail       func(*Session, string, string) error
165
-		rcpt       func(*Session, string, string) error
166
-		data       func(*Session) error
167
-		dataline   func(*Session, string) error
168
-		eom        func(*Session, uint32) error
169
-		reset      func(*Session) error
170
-		disconnect func(*Session) error
171
-		commit     func(*Session) error
172
-	}
187
+	hooks   int
188
+	flags   int
189
+	ready   bool
173 190
 	session *lru.Cache
174 191
 }
175 192
 
176 193
 func (f *Filter) OnConnect(fn func(*Session, *ConnectQuery) error) {
177
-	f.hook.connect = fn
194
+	f.Connect = fn
178 195
 	f.hooks |= HookConnect
179 196
 }
180 197
 
181 198
 func (f *Filter) OnHELO(fn func(*Session, string) error) {
182
-	f.hook.helo = fn
199
+	f.HELO = fn
183 200
 	f.hooks |= HookHELO
184 201
 }
185 202
 
186 203
 func (f *Filter) OnMAIL(fn func(*Session, string, string) error) {
187
-	f.hook.mail = fn
204
+	f.MAIL = fn
188 205
 	f.hooks |= HookMAIL
189 206
 }
190 207
 
191 208
 func (f *Filter) OnRCPT(fn func(*Session, string, string) error) {
192
-	f.hook.rcpt = fn
209
+	f.RCPT = fn
193 210
 	f.hooks |= HookRCPT
194 211
 }
195 212
 
196 213
 func (f *Filter) OnDATA(fn func(*Session) error) {
197
-	f.hook.data = fn
214
+	f.DATA = fn
198 215
 	f.hooks |= HookDATA
199 216
 }
200 217
 
201 218
 func (f *Filter) OnDataLine(fn func(*Session, string) error) {
202
-	f.hook.dataline = fn
219
+	f.DataLine = fn
203 220
 	f.hooks |= HookDataLine
204 221
 }
205 222
 
@@ -218,6 +235,35 @@ func (f *Filter) Register() error {
218 235
 		return err
219 236
 	}
220 237
 
238
+	// Fill hooks mask
239
+	if f.Connect != nil {
240
+		f.hooks |= HookConnect
241
+	}
242
+	if f.HELO != nil {
243
+		f.hooks |= HookHELO
244
+	}
245
+	if f.MAIL != nil {
246
+		f.hooks |= HookMAIL
247
+	}
248
+	if f.RCPT != nil {
249
+		f.hooks |= HookRCPT
250
+	}
251
+	if f.DATA != nil {
252
+		f.hooks |= HookDATA
253
+	}
254
+	if f.DataLine != nil {
255
+		f.hooks |= HookDataLine
256
+	}
257
+	if f.EOM != nil {
258
+		f.hooks |= HookEOM
259
+	}
260
+	if f.Disconnect != nil {
261
+		f.hooks |= HookDisconnect
262
+	}
263
+	if f.Commit != nil {
264
+		f.hooks |= HookCommit
265
+	}
266
+
221 267
 	if t, ok := filterTypeName[f.m.Type]; ok {
222 268
 		log.Printf("filter: imsg %s\n", t)
223 269
 	} else {
@@ -388,8 +434,8 @@ func (f *Filter) handleQuery() (err error) {
388 434
 		}
389 435
 
390 436
 		log.Printf("query connect: %s\n", query)
391
-		if f.hook.connect != nil {
392
-			return f.hook.connect(s, &query)
437
+		if f.Connect != nil {
438
+			return f.Connect(s, &query)
393 439
 		}
394 440
 
395 441
 		log.Printf("filter: WARNING: no connect callback\n")
@@ -401,8 +447,8 @@ func (f *Filter) handleQuery() (err error) {
401 447
 		}
402 448
 
403 449
 		log.Printf("query HELO: %q\n", line)
404
-		if f.hook.helo != nil {
405
-			return f.hook.helo(s, line)
450
+		if f.HELO != nil {
451
+			return f.HELO(s, line)
406 452
 		}
407 453
 
408 454
 		log.Printf("filter: WARNING: no HELO callback\n")
@@ -415,8 +461,8 @@ func (f *Filter) handleQuery() (err error) {
415 461
 		}
416 462
 
417 463
 		log.Printf("query MAIL: %s\n", user+"@"+domain)
418
-		if f.hook.mail != nil {
419
-			return f.hook.mail(s, user, domain)
464
+		if f.MAIL != nil {
465
+			return f.MAIL(s, user, domain)
420 466
 		}
421 467
 
422 468
 		log.Printf("filter: WARNING: no MAIL callback\n")
@@ -429,16 +475,16 @@ func (f *Filter) handleQuery() (err error) {
429 475
 		}
430 476
 
431 477
 		log.Printf("query RCPT: %s\n", user+"@"+domain)
432
-		if f.hook.rcpt != nil {
433
-			return f.hook.rcpt(s, user, domain)
478
+		if f.RCPT != nil {
479
+			return f.RCPT(s, user, domain)
434 480
 		}
435 481
 
436 482
 		log.Printf("filter: WARNING: no RCPT callback\n")
437 483
 		return f.respond(s, FilterOK, 0, "")
438 484
 
439 485
 	case QueryDATA:
440
-		if f.hook.data != nil {
441
-			return f.hook.data(s)
486
+		if f.DATA != nil {
487
+			return f.DATA(s)
442 488
 		}
443 489
 
444 490
 		log.Printf("filter: WARNING: no DATA callback\n")
@@ -450,8 +496,8 @@ func (f *Filter) handleQuery() (err error) {
450 496
 			return
451 497
 		}
452 498
 
453
-		if f.hook.eom != nil {
454
-			return f.hook.eom(s, dataLen)
499
+		if f.EOM != nil {
500
+			return f.EOM(s, dataLen)
455 501
 		}
456 502
 
457 503
 		log.Printf("filter: WARNING: no EOM callback\n")

+ 31
- 0
filter_test.go View File

@@ -0,0 +1,31 @@
1
+package opensmtpd
2
+
3
+import "strings"
4
+
5
+func ExampleFilter() {
6
+	// Build our filter
7
+	filter := &Filter{
8
+		HELO: func(session *Session, helo string) error {
9
+			if helo == "test" {
10
+				return session.Reject()
11
+			}
12
+			return session.Accept()
13
+		},
14
+	}
15
+
16
+	// Add another hook
17
+	filter.OnMAIL(func(session *Session, user, domain string) error {
18
+		if strings.ToLower(domain) == "example.org" {
19
+			return session.Reject()
20
+		}
21
+		return session.Accept()
22
+	})
23
+
24
+	// Register our filter with smtpd
25
+	if err := filter.Register(); err != nil {
26
+		panic(err)
27
+	}
28
+
29
+	// And keep serving until smtpd stops
30
+	filter.Serve()
31
+}

+ 1
- 0
table.go View File

@@ -43,6 +43,7 @@ func procTableName(t uint32) string {
43 43
 	return fmt.Sprintf("UNKNOWN %d", t)
44 44
 }
45 45
 
46
+// Table implements the OpenSMTPD table API
46 47
 type Table struct {
47 48
 	// Update callback
48 49
 	Update func() (int, error)

+ 23
- 0
table_test.go View File

@@ -0,0 +1,23 @@
1
+package opensmtpd
2
+
3
+func ExampleTable() {
4
+	// In smtpd.conf:
5
+	//
6
+	//   table aliases <name-of-filter>:
7
+	//   accept for local alias <aliases> ...
8
+
9
+	aliases := map[string]string{
10
+		"root": "user@example.org",
11
+	}
12
+
13
+	table := &Table{
14
+		Lookup: func(service int, params Dict, key string) (string, error) {
15
+			// We are only valid for aliases
16
+			if service&ServiceAlias != 0 {
17
+				return aliases[key], nil
18
+			}
19
+			return "", nil
20
+		},
21
+	}
22
+	table.Serve()
23
+}

Loading…
Cancel
Save