Merge remote-tracking branch 'github/master'
[mono.git] / mcs / class / System / Mono.Net.Dns / DnsHeader.cs
1 //
2 // Mono.Net.Dns.DnsHeader
3 //
4 // Authors:
5 //      Gonzalo Paniagua Javier (gonzalo.mono@gmail.com)
6 //
7 // Copyright 2011 Gonzalo Paniagua Javier
8 //
9 // Licensed under the Apache License, Version 2.0 (the "License");
10 // you may not use this file except in compliance with the License.
11 // You may obtain a copy of the License at
12 //
13 // http://www.apache.org/licenses/LICENSE-2.0
14 //
15 // Unless required by applicable law or agreed to in writing, software
16 // distributed under the License is distributed on an "AS IS" BASIS,
17 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 // See the License for the specific language governing permissions and
19 // limitations under the License.
20 //
21 using System;
22 using System.Collections.Generic;
23 using System.Text;
24
25 namespace Mono.Net.Dns {
26         class DnsHeader {
27                 public const int DnsHeaderLength = 12;
28                 ArraySegment<byte> bytes;
29
30                 public DnsHeader (byte [] bytes)
31                         : this (bytes, 0)
32                 {
33                 }
34
35                 public DnsHeader (byte [] bytes, int offset)
36                         : this (new ArraySegment<byte> (bytes, offset, DnsHeaderLength))
37                 {
38                 }
39
40                 public DnsHeader (ArraySegment<byte> segment)
41                 {
42                         if (segment.Count != DnsHeaderLength)
43                                 throw new ArgumentException ("Count must be 12", "segment");
44
45                         bytes = segment;
46                 }
47
48                 public void Clear ()
49                 {
50                         for (int i = 0; i < DnsHeaderLength; i++)
51                                 bytes.Array [i + bytes.Offset] = 0;
52                 }
53
54                 public ushort ID {
55                         get {
56                                 return (ushort)(bytes.Array [bytes.Offset] * 256 + bytes.Array [bytes.Offset + 1]);
57                         }
58                         set {
59                                 bytes.Array [bytes.Offset] = (byte) ((value & 0x0ff00) >> 8);
60                                 bytes.Array [bytes.Offset + 1] = (byte) (value & 0x0ff);
61                         }
62                 }
63
64                 public bool IsQuery {
65                         get { return ((bytes.Array [2 + bytes.Offset] & 0x80) != 0); }
66                         set {
67                                 if (!value) {
68                                         bytes.Array [2 + bytes.Offset] |= 0x80;
69                                 } else {
70                                         bytes.Array [2 + bytes.Offset] &= 0x7f;
71                                 }
72                         }
73                 }
74
75                 public DnsOpCode OpCode {
76                         get { return (DnsOpCode) ((bytes.Array [2 + bytes.Offset] & 0x78) >> 3); }
77                         set {
78                                 if (!Enum.IsDefined (typeof (DnsOpCode), value))
79                                         throw new ArgumentOutOfRangeException ("value", "Invalid DnsOpCode value");
80
81                                 int v = (int) value;
82                                 v <<= 3;
83                                 int prev = (bytes.Array [2 + bytes.Offset] & 0x87);
84                                 v |= prev;
85                                 bytes.Array [2 + bytes.Offset] = (byte) v;
86                         }
87                 }
88
89                 public bool AuthoritativeAnswer {
90                         get { return (bytes.Array [2 + bytes.Offset] & 4) != 0; }
91                         set {
92                                 if(value) {
93                                         bytes.Array [2 + bytes.Offset] |= 4;
94                                 } else {
95                                         bytes.Array [2 + bytes.Offset] &= 0xFB;
96                                 }
97                         }
98                 }
99
100                 public bool Truncation {
101                         get { return (bytes.Array [2 + bytes.Offset] & 2) != 0; }
102                         set {
103                                 if(value) {
104                                         bytes.Array [2 + bytes.Offset] |= 2;
105                                 } else {
106                                         bytes.Array [2 + bytes.Offset] &= 0xFD;
107                                 }
108                         }
109                 }
110
111                 public bool RecursionDesired {
112                         get { return (bytes.Array [2 + bytes.Offset] & 1) != 0; }
113                         set {
114                                 if(value) {
115                                         bytes.Array [2 + bytes.Offset] |= 1;
116                                 } else {
117                                         bytes.Array [2 + bytes.Offset] &= 0xFE;
118                                 }
119                         }
120                 }
121
122                 public bool RecursionAvailable {
123                         get { return (bytes.Array [3 + bytes.Offset] & 0x80) != 0; }
124                         set {
125                                 if(value) {
126                                         bytes.Array [3 + bytes.Offset] |= 0x80;
127                                 } else {
128                                         bytes.Array [3 + bytes.Offset] &= 0x7F;
129                                 }
130                         }
131                 }
132
133                 // TODO: Add AuthenticData and Checking Disabled (bit 10 and 11 of Z)
134                 public int ZReserved {
135                         get { return (bytes.Array [3 + bytes.Offset] & 0x70) >> 4; }
136                         set {
137                                 if(value < 0 || value > 7) {
138                                         throw new ArgumentOutOfRangeException("value", "Must be between 0 and 7");
139                                 }
140                                 bytes.Array [3 + bytes.Offset] &= 0x8F;
141                                 bytes.Array [3 + bytes.Offset] |= (byte) ((value << 4) & 0x70);
142                         }
143                 }
144
145                 public DnsRCode RCode {
146                         get { return (DnsRCode) (bytes.Array [3 + bytes.Offset] & 0x0f); }
147                         set {
148                                 int v = (int)value;
149                                 //Info: Values > 15 are encoded in other records (OPT, TSIG, TKEY)
150                                 if (v < 0 || v > 15)
151                                         throw new ArgumentOutOfRangeException("value", "Must be between 0 and 15");
152
153                                 bytes.Array [3 + bytes.Offset] &= 0x0f;
154                                 bytes.Array [3 + bytes.Offset] |= (byte) v;
155                         }
156                 }
157
158                 static ushort GetUInt16 (byte [] bytes, int offset)
159                 {
160                         return (ushort)(bytes [offset] * 256 + bytes [offset + 1]);
161                 }
162
163                 static void SetUInt16 (byte [] bytes, int offset, ushort val)
164                 {
165                         bytes [offset] = (byte) ((val & 0x0ff00) >> 8);
166                         bytes [offset + 1] = (byte) (val & 0x0ff);
167                 }
168
169                 public ushort QuestionCount {
170                         get { return GetUInt16 (bytes.Array, 4); }
171                         set { SetUInt16 (bytes.Array, 4, value); }
172                 }
173
174                 public ushort AnswerCount {
175                         get { return GetUInt16 (bytes.Array, 6); }
176                         set { SetUInt16 (bytes.Array, 6, value); }
177                 }
178
179                 public ushort AuthorityCount {
180                         get { return GetUInt16 (bytes.Array, 8); }
181                         set { SetUInt16 (bytes.Array, 8, value); }
182                 }
183
184                 public ushort AdditionalCount {
185                         get { return GetUInt16 (bytes.Array, 10); }
186                         set { SetUInt16 (bytes.Array, 10, value); }
187                 }
188
189                 public override string ToString ()
190                 {
191                         StringBuilder sb = new StringBuilder();
192                         sb.AppendFormat ("ID: {0} QR: {1} Opcode: {2} AA: {3} TC: {4} RD: {5} RA: {6} \r\nRCode: {7} ",
193                                         ID, IsQuery, OpCode, AuthoritativeAnswer, Truncation, RecursionDesired,
194                                         RecursionAvailable, RCode);
195                         sb.AppendFormat ("Q: {0} A: {1} NS: {2} AR: {3}\r\n", QuestionCount, AnswerCount, AuthorityCount, AdditionalCount);
196                         return sb.ToString();
197                 }
198         }
199 }
200