github.com/iDigitalFlame/xmt@v0.5.4/c2/job.go (about) 1 //go:build !implant 2 // +build !implant 3 4 // Copyright (C) 2020 - 2023 iDigitalFlame 5 // 6 // This program is free software: you can redistribute it and/or modify 7 // it under the terms of the GNU General Public License as published by 8 // the Free Software Foundation, either version 3 of the License, or 9 // any later version. 10 // 11 // This program is distributed in the hope that it will be useful, 12 // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 // GNU General Public License for more details. 15 // 16 // You should have received a copy of the GNU General Public License 17 // along with this program. If not, see <https://www.gnu.org/licenses/>. 18 // 19 20 package c2 21 22 import ( 23 "time" 24 25 "github.com/iDigitalFlame/xmt/com" 26 ) 27 28 // These are status values that indicate the general status of the Job. 29 const ( 30 StatusWaiting status = 0 31 StatusAccepted status = iota 32 StatusReceiving 33 StatusCompleted 34 StatusError 35 StatusCanceled 36 ) 37 38 // Job is a struct that is used to track and manage Tasks given to Session 39 // Clients. 40 // 41 // This struct has function callbacks that can be used to watch for completion 42 // and offers a Wait function to pause execution until a response is received. 43 // 44 // This struct is always empty for implants. 45 type Job struct { 46 Start, Complete time.Time 47 48 Update func(*Job) 49 Result *com.Packet 50 done chan struct{} 51 s *Session 52 53 Error string 54 ID, Frags, Current uint16 55 56 Type uint8 57 Status status 58 } 59 type status uint8 60 61 // Wait will block until the Job is completed or the parent Server is shutdown. 62 func (j *Job) Wait() { 63 if j == nil || j.done == nil { 64 return 65 } 66 <-j.done 67 } 68 69 // Cancel will stop the current Job in-flight and will remove it from the Task 70 // queue. Any threads waiting on this Job will return once this function completes. 71 // 72 // This does NOT prevent the client Session from running it, but will close 73 // out all receiving channels and any received data will be marked as an un-tracked 74 // Job. 75 // 76 // This is the only method that results in a Status of Canceled. 77 func (j *Job) Cancel() { 78 if j == nil || j.done == nil { 79 return 80 } 81 if j.Status >= StatusCompleted { 82 // Something happened and didn't close done. 83 if j.done != nil { 84 // NOTE(dij): I don't think this will panic, but I need to test to 85 // be 100% sure. 86 close(j.done) 87 } 88 return 89 } 90 j.s.lock.Lock() 91 if j.s.jobs == nil || len(j.s.jobs) == 0 { 92 close(j.done) 93 j.Status, j.done = StatusCanceled, nil 94 // NOTE(dij): We're using the Session Mutex to protect all Jobs since it's 95 // the only non-OOB place we'd cancel em at. 96 j.s.lock.Unlock() 97 return 98 } 99 if _, ok := j.s.jobs[j.ID]; !ok { 100 close(j.done) 101 j.Status, j.done = StatusCanceled, nil 102 j.s.lock.Unlock() 103 // NOTE(dij): I know this does a lot of work while the Mutex is spinning, 104 // but it stays in sync. 105 return 106 } 107 j.s.jobs[j.ID] = nil 108 delete(j.s.jobs, j.ID) 109 close(j.done) 110 j.done = nil 111 if j.s.lock.Unlock(); j.Update == nil { 112 return 113 } 114 j.s.m.queue(event{j: j, jf: j.Update}) 115 } 116 117 // IsDone returns true when the Job has received a response, has error out or 118 // was canceled. Use the Status field to determine the state of the Job. 119 func (j *Job) IsDone() bool { 120 if j == nil || j.done == nil { 121 return true 122 } 123 select { 124 case <-j.done: 125 return true 126 default: 127 } 128 return false 129 } 130 131 // IsError returns true when the Job has received a response, but the response 132 // is an error. 133 func (j *Job) IsError() bool { 134 if j == nil { 135 return false 136 } 137 if j.IsDone() { 138 return len(j.Error) > 0 139 } 140 return false 141 } 142 143 // Session returns the Session that is associated with this Job. 144 func (j *Job) Session() *Session { 145 return j.s 146 }