Package opensmtpd implements OpenSMTPD-extras in Go https://godoc.org/pkg/maze.io/x/opensmtpd
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

table.go 5.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. package opensmtpd
  2. import (
  3. "bytes"
  4. "encoding/binary"
  5. "fmt"
  6. "io"
  7. "log"
  8. "net"
  9. "os"
  10. )
  11. const (
  12. procTableOK = iota
  13. procTableFail
  14. procTableOpen
  15. procTableClose
  16. procTableUpdate
  17. procTableCheck
  18. procTableLookup
  19. procTableFetch
  20. )
  21. var procTableTypeName = map[uint32]string{
  22. procTableOK: "PROC_TABLE_OK",
  23. procTableFail: "PROC_TABLE_FAIL",
  24. procTableOpen: "PROC_TABLE_OPEN",
  25. procTableClose: "PROC_TABLE_CLOSE",
  26. procTableUpdate: "PROC_TABLE_UPDATE",
  27. procTableCheck: "PROC_TABLE_CHECK",
  28. procTableLookup: "PROC_TABLE_LOOKUP",
  29. procTableFetch: "PROC_TABLE_FETCH",
  30. }
  31. func procTableName(t uint32) string {
  32. if s, ok := procTableTypeName[t]; ok {
  33. return s
  34. }
  35. return fmt.Sprintf("UNKNOWN %d", t)
  36. }
  37. // Table implements the OpenSMTPD table API
  38. type Table struct {
  39. // Update callback
  40. Update func() (int, error)
  41. // Check callback
  42. Check func(service int, params Dict, key string) (int, error)
  43. // Lookup callback
  44. Lookup func(service int, params Dict, key string) (string, error)
  45. // Fetch callback
  46. Fetch func(service int, params Dict) (string, error)
  47. // Close callback, called at stop
  48. Close func() error
  49. c net.Conn
  50. m *message
  51. closed bool
  52. }
  53. func (t *Table) Serve() error {
  54. var err error
  55. if t.c, err = newConn(0); err != nil {
  56. return err
  57. }
  58. t.m = new(message)
  59. for !t.closed {
  60. if err = t.m.ReadFrom(t.c); err != nil {
  61. if err.Error() != "resource temporarily unavailable" {
  62. return fmt.Errorf("read error: %v", err)
  63. }
  64. }
  65. debugf("table: %s", procTableName(t.m.Header.Type))
  66. if err = t.dispatch(); err != nil {
  67. return fmt.Errorf("dispatch error: %v", err)
  68. }
  69. }
  70. return err
  71. }
  72. type tableOpenParams struct {
  73. Version uint32
  74. Name [maxLineSize]byte
  75. }
  76. func (t *Table) dispatch() (err error) {
  77. switch t.m.Header.Type {
  78. case procTableOpen:
  79. /*
  80. var op tableOpenParams
  81. if err = t.getMessage(&op, maxLineSize+4); err != nil {
  82. return
  83. }
  84. if op.Version != TableVersion {
  85. fatalf("table: bad API version %d (we support %d)", op.Version, TableVersion)
  86. }
  87. if bytes.IndexByte(op.Name[:], 0) <= 0 {
  88. fatal("table: no name supplied")
  89. }
  90. */
  91. var version uint32
  92. if version, err = t.m.GetUint32(); err != nil {
  93. return
  94. } else if version != TableVersion {
  95. fatalf("table: expected API version %d, got %d", TableVersion, version)
  96. }
  97. var name string
  98. if name, err = t.m.GetString(); err != nil {
  99. return
  100. } else if name == "" {
  101. fatal("table: no name supplied by smtpd!?")
  102. }
  103. debugf("table: version=%d name=%q\n", version, name)
  104. m := new(message)
  105. m.Header.Type = procTableOK
  106. m.Header.Len = imsgHeaderSize
  107. m.Header.PID = uint32(os.Getpid())
  108. if err = m.WriteTo(t.c); err != nil {
  109. return
  110. }
  111. case procTableUpdate:
  112. var r = 1
  113. if t.Update != nil {
  114. if r, err = t.Update(); err != nil {
  115. return
  116. }
  117. }
  118. m := new(message)
  119. m.Header.Type = procTableOK
  120. m.PutInt(r)
  121. if err = m.WriteTo(t.c); err != nil {
  122. return
  123. }
  124. case procTableClose:
  125. if t.Close != nil {
  126. if err = t.Close(); err != nil {
  127. return
  128. }
  129. }
  130. t.closed = true
  131. return
  132. case procTableCheck:
  133. var service int
  134. if service, err = t.m.GetInt(); err != nil {
  135. return
  136. }
  137. var params Dict
  138. if params, err = t.getParams(); err != nil {
  139. return
  140. }
  141. var key string
  142. if key, err = t.m.GetString(); err != nil {
  143. return
  144. }
  145. debugf("table_check: service=%s,params=%+v,key=%q",
  146. serviceName(service), params, key)
  147. var r = -1
  148. if t.Check != nil {
  149. if r, err = t.Check(service, params, key); err != nil {
  150. return
  151. }
  152. }
  153. log.Printf("table_check: result=%d\n", r)
  154. m := new(message)
  155. m.Header.Type = procTableOK
  156. m.Header.PID = uint32(os.Getpid())
  157. m.PutInt(r)
  158. if err = m.WriteTo(t.c); err != nil {
  159. return
  160. }
  161. case procTableLookup:
  162. var service int
  163. if service, err = t.m.GetInt(); err != nil {
  164. return
  165. }
  166. var params Dict
  167. if params, err = t.getParams(); err != nil {
  168. return
  169. }
  170. var key string
  171. if key, err = t.m.GetString(); err != nil {
  172. return
  173. }
  174. debugf("table_lookup: service=%s,params=%+v,key=%q",
  175. serviceName(service), params, key)
  176. var val string
  177. if t.Lookup != nil {
  178. if val, err = t.Lookup(service, params, key); err != nil {
  179. return
  180. }
  181. }
  182. m := new(message)
  183. m.Header.Type = procTableOK
  184. m.Header.PID = uint32(os.Getpid())
  185. if val == "" {
  186. m.PutInt(-1)
  187. } else {
  188. m.PutInt(1)
  189. m.PutString(val)
  190. }
  191. if err = m.WriteTo(t.c); err != nil {
  192. return
  193. }
  194. case procTableFetch:
  195. var service int
  196. if service, err = t.m.GetInt(); err != nil {
  197. return
  198. }
  199. var params Dict
  200. if params, err = t.getParams(); err != nil {
  201. return
  202. }
  203. debugf("table_fetch: service=%s,params=%+v",
  204. serviceName(service), params)
  205. var val string
  206. if t.Fetch != nil {
  207. if val, err = t.Fetch(service, params); err != nil {
  208. return
  209. }
  210. }
  211. m := new(message)
  212. m.Header.Type = procTableOK
  213. m.Header.PID = uint32(os.Getpid())
  214. if val == "" {
  215. m.PutInt(-1)
  216. } else {
  217. m.PutInt(1)
  218. m.PutString(val)
  219. }
  220. if err = m.WriteTo(t.c); err != nil {
  221. return
  222. }
  223. }
  224. return nil
  225. }
  226. func (t *Table) getMessage(data interface{}, size int) (err error) {
  227. buf := make([]byte, size)
  228. if _, err = io.ReadFull(t.c, buf); err != nil {
  229. return
  230. }
  231. return binary.Read(bytes.NewBuffer(buf), binary.LittleEndian, data)
  232. }
  233. func (t *Table) getParams() (params Dict, err error) {
  234. var count uint64
  235. if count, err = t.m.GetSize(); err != nil {
  236. return
  237. }
  238. debugf("params: %d pairs", count)
  239. params = make(Dict, count)
  240. if count == 0 {
  241. return
  242. }
  243. var k, v string
  244. for ; count != 0; count-- {
  245. if k, err = t.m.GetString(); err != nil {
  246. return
  247. }
  248. if v, err = t.m.GetString(); err != nil {
  249. return
  250. }
  251. params[k] = v
  252. }
  253. return
  254. }